00001 /* 00002 * Phusion Passenger - http://www.modrails.com/ 00003 * Copyright (C) 2008 Phusion 00004 * 00005 * Phusion Passenger is a trademark of Hongli Lai & Ninh Bui. 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; version 2 of the License. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00019 */ 00020 #ifndef _PASSENGER_UTILS_H_ 00021 #define _PASSENGER_UTILS_H_ 00022 00023 #include <boost/shared_ptr.hpp> 00024 #include <sys/types.h> 00025 #include <sys/stat.h> 00026 #include <string> 00027 #include <vector> 00028 #include <utility> 00029 #include <sstream> 00030 #include <cstdio> 00031 #include <climits> 00032 #include <cstdlib> 00033 #include <cstring> 00034 #include <errno.h> 00035 #include <unistd.h> 00036 #include "Exceptions.h" 00037 00038 typedef struct CachedMultiFileStat CachedMultiFileStat; 00039 00040 namespace Passenger { 00041 00042 using namespace std; 00043 using namespace boost; 00044 00045 /** Enumeration which indicates what kind of file a file is. */ 00046 typedef enum { 00047 /** The file doesn't exist. */ 00048 FT_NONEXISTANT, 00049 /** A regular file or a symlink to a regular file. */ 00050 FT_REGULAR, 00051 /** A directory. */ 00052 FT_DIRECTORY, 00053 /** Something else, e.g. a pipe or a socket. */ 00054 FT_OTHER 00055 } FileType; 00056 00057 /** 00058 * Convenience shortcut for creating a <tt>shared_ptr</tt>. 00059 * Instead of: 00060 * @code 00061 * shared_ptr<Foo> foo; 00062 * ... 00063 * foo = shared_ptr<Foo>(new Foo()); 00064 * @endcode 00065 * one can write: 00066 * @code 00067 * shared_ptr<Foo> foo; 00068 * ... 00069 * foo = ptr(new Foo()); 00070 * @endcode 00071 * 00072 * @param pointer The item to put in the shared_ptr object. 00073 * @ingroup Support 00074 */ 00075 template<typename T> shared_ptr<T> 00076 ptr(T *pointer) { 00077 return shared_ptr<T>(pointer); 00078 } 00079 00080 /** 00081 * Used internally by toString(). Do not use directly. 00082 * 00083 * @internal 00084 */ 00085 template<typename T> 00086 struct AnythingToString { 00087 string operator()(T something) { 00088 stringstream s; 00089 s << something; 00090 return s.str(); 00091 } 00092 }; 00093 00094 /** 00095 * Used internally by toString(). Do not use directly. 00096 * 00097 * @internal 00098 */ 00099 template<> 00100 struct AnythingToString< vector<string> > { 00101 string operator()(const vector<string> &v) { 00102 string result("["); 00103 vector<string>::const_iterator it; 00104 unsigned int i; 00105 for (it = v.begin(), i = 0; it != v.end(); it++, i++) { 00106 result.append("'"); 00107 result.append(*it); 00108 if (i == v.size() - 1) { 00109 result.append("'"); 00110 } else { 00111 result.append("', "); 00112 } 00113 } 00114 result.append("]"); 00115 return result; 00116 } 00117 }; 00118 00119 /** 00120 * Convert anything to a string. 00121 * 00122 * @param something The thing to convert. 00123 * @ingroup Support 00124 */ 00125 template<typename T> string 00126 toString(T something) { 00127 return AnythingToString<T>()(something); 00128 } 00129 00130 /** 00131 * Converts the given string to an integer. 00132 * @ingroup Support 00133 */ 00134 int atoi(const string &s); 00135 00136 /** 00137 * Converts the given string to a long integer. 00138 * @ingroup Support 00139 */ 00140 long atol(const string &s); 00141 00142 /** 00143 * Split the given string using the given separator. 00144 * 00145 * @param str The string to split. 00146 * @param sep The separator to use. 00147 * @param output The vector to write the output to. 00148 * @ingroup Support 00149 */ 00150 void split(const string &str, char sep, vector<string> &output); 00151 00152 /** 00153 * Check whether the specified file exists. 00154 * 00155 * @param filename The filename to check. 00156 * @param mstat A CachedMultiFileStat object, if you want to use cached statting. 00157 * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL. 00158 * @return Whether the file exists. 00159 * @throws FileSystemException Unable to check because of a filesystem error. 00160 * @ingroup Support 00161 */ 00162 bool fileExists(const char *filename, CachedMultiFileStat *mstat = 0, 00163 unsigned int throttleRate = 0); 00164 00165 /** 00166 * Check whether 'filename' exists and what kind of file it is. 00167 * 00168 * @param filename The filename to check. 00169 * @param mstat A CachedMultiFileStat object, if you want to use cached statting. 00170 * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL. 00171 * @return The file type. 00172 * @throws FileSystemException Unable to check because of a filesystem error. 00173 * @ingroup Support 00174 */ 00175 FileType getFileType(const char *filename, CachedMultiFileStat *mstat = 0, 00176 unsigned int throttleRate = 0); 00177 00178 /** 00179 * Find the location of the Passenger spawn server script. 00180 * If passengerRoot is given, t T 00181 * 00182 * @param passengerRoot The Passenger root folder. If NULL is given, then 00183 * the spawn server is found by scanning $PATH. For security reasons, 00184 * only absolute paths are scanned. 00185 * @return An absolute path to the spawn server script, or 00186 * an empty string on error. 00187 * @throws FileSystemException Unable to access parts of the filesystem. 00188 * @ingroup Support 00189 */ 00190 string findSpawnServer(const char *passengerRoot = NULL); 00191 00192 /** 00193 * Find the location of the Passenger ApplicationPool server 00194 * executable. 00195 * 00196 * @param passengerRoot The Passenger root folder. 00197 * @return An absolute path to the executable. 00198 * @throws FileSystemException Unable to access parts of the filesystem. 00199 * @pre passengerRoot != NULL 00200 * @ingroup Support 00201 */ 00202 string findApplicationPoolServer(const char *passengerRoot); 00203 00204 /** 00205 * Returns a canonical version of the specified path. All symbolic links 00206 * and relative path elements are resolved. 00207 * 00208 * @throws FileSystemException Something went wrong. 00209 * @ingroup Support 00210 */ 00211 string canonicalizePath(const string &path); 00212 00213 /** 00214 * Escape the given raw string into an XML value. 00215 * 00216 * @throws std::bad_alloc Something went wrong. 00217 * @ingroup Support 00218 */ 00219 string escapeForXml(const string &input); 00220 00221 /** 00222 * Given a username that's supposed to be the "lowest user" in the user switching mechanism, 00223 * checks whether this username exists. If so, this users's UID and GID will be stored into 00224 * the arguments of the same names. If not, <em>uid</em> and <em>gid</em> will be set to 00225 * the UID and GID of the "nobody" user. If that user doesn't exist either, then <em>uid</em> 00226 * and <em>gid</em> will be set to -1. 00227 */ 00228 void determineLowestUserAndGroup(const string &user, uid_t &uid, gid_t &gid); 00229 00230 /** 00231 * Return the path name for the directory in which the system stores general 00232 * temporary files. This is usually "/tmp", but might be something else depending 00233 * on some environment variables. 00234 * 00235 * @ensure result != NULL 00236 * @ingroup Support 00237 */ 00238 const char *getSystemTempDir(); 00239 00240 /** 00241 * Return the path name for the directory in which Phusion Passenger-specific 00242 * temporary files are to be stored. This directory is unique for this instance 00243 * of the web server in which Phusion Passenger is running. 00244 * 00245 * If the environment variable PASSENGER_INSTANCE_TEMP_DIR is set, then that value 00246 * will be returned. If this environment variable is not set, then it will be set 00247 * with the return value. 00248 * 00249 * To bypass the usage of the PASSENGER_INSTANCE_TEMP_DIR environment variable, 00250 * set 'bypassCache' to true. 00251 * 00252 * @param bypassCache Whether PASSENGER_INSTANCE_TEMP_DIR should be bypassed. 00253 * @param systemTempDir The directory under which the Phusion Passenger-specific 00254 * temp directory should be located. If set to the empty string, 00255 * then the return value of getSystemTempDir() will be used. 00256 * @ensure !result.empty() 00257 * @ingroup Support 00258 */ 00259 string getPassengerTempDir(bool bypassCache = false, const string &systemTempDir = ""); 00260 00261 /* Create a temp directory under <em>systemTempDir</em>, for storing Phusion 00262 * Passenger-specific temp files, such as temporarily buffered uploads, 00263 * sockets for backend processes, etc. This call also sets the 00264 * PASSENGER_INSTANCE_TEMP_DIR environment variable, which allows subprocesses 00265 * to find this temp directory. 00266 * 00267 * The created temp directory will have several subdirectories: 00268 * - webserver_private - for storing the web server's buffered uploads. 00269 * - info - for storing files that allow external tools to query information 00270 * about a running Phusion Passenger instance. 00271 * - backends - for storing Unix sockets created by backend processes. 00272 * - var - for storing all other kinds of temp files that the backend processes 00273 * create. 00274 * 00275 * If a (sub)directory already exists, then it will not result in an error. 00276 * 00277 * The <em>userSwitching</em> and <em>lowestUser</em> arguments passed to 00278 * this method are used for determining the optimal permissions for the 00279 * (sub)directories. The permissions will be set as tightly as possible based 00280 * on the values. The <em>workerUid</em> and <em>workerGid</em> arguments 00281 * will be used for determining the owner of certain subdirectories. 00282 * 00283 * @note You should only call this method inside the web server's master 00284 * process. In case of Apache, this is the Apache control process, 00285 * the one that tends to run as root. This is because this function 00286 * will set directory permissions and owners/groups, which may require 00287 * root privileges. 00288 * 00289 * @param systemTempDir The directory under which the Phusion Passenger-specific 00290 * temp directory should be created. You should normally 00291 * specify the return value of getSystemTempDir(). 00292 * @param userSwitching Whether user switching is turned on. 00293 * @param lowestUser The user that the spawn manager and the pool server will 00294 * run as, if user switching is turned off. 00295 * @param workerUid The UID that the web server's worker processes are running 00296 * as. On Apache, this is the UID that's associated with the 00297 * 'User' directive. 00298 * @param workerGid The GID that the web server's worker processes are running 00299 * as. On Apache, this is the GID that's associated with the 00300 * 'Group' directive. 00301 * @throws IOException Something went wrong. 00302 * @throws SystemException Something went wrong. 00303 * @throws FileSystemException Something went wrong. 00304 */ 00305 void createPassengerTempDir(const string &systemTempDir, bool userSwitching, 00306 const string &lowestUser, 00307 uid_t workerUid, gid_t workerGid); 00308 00309 /** 00310 * Create the directory at the given path, creating intermediate directories 00311 * if necessary. The created directories' permissions are as specified by the 00312 * 'mode' parameter. You can specify this directory's owner and group through 00313 * the 'owner' and 'group' parameters. A value of -1 for 'owner' or 'group' 00314 * means that the owner/group should not be changed. 00315 * 00316 * If 'path' already exists, then nothing will happen. 00317 * 00318 * @throws IOException Something went wrong. 00319 * @throws SystemException Something went wrong. 00320 * @throws FileSystemException Something went wrong. 00321 */ 00322 void makeDirTree(const string &path, const char *mode = "u=rwx,g=,o=", uid_t owner = -1, gid_t group = -1); 00323 00324 /** 00325 * Remove an entire directory tree recursively. 00326 * 00327 * @throws FileSystemException Something went wrong. 00328 */ 00329 void removeDirTree(const string &path); 00330 00331 /** 00332 * Check whether the specified directory is a valid Ruby on Rails 00333 * application root directory. 00334 * 00335 * @param mstat A CachedMultiFileStat object, if you want to use cached statting. 00336 * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL. 00337 * @throws FileSystemException Unable to check because of a system error. 00338 * @ingroup Support 00339 */ 00340 bool verifyRailsDir(const string &dir, CachedMultiFileStat *mstat = 0, 00341 unsigned int throttleRate = 0); 00342 00343 /** 00344 * Check whether the specified directory is a valid Rack application 00345 * root directory. 00346 * 00347 * @param mstat A CachedMultiFileStat object, if you want to use cached statting. 00348 * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL. 00349 * @throws FileSystemException Unable to check because of a filesystem error. 00350 * @ingroup Support 00351 */ 00352 bool verifyRackDir(const string &dir, CachedMultiFileStat *mstat = 0, 00353 unsigned int throttleRate = 0); 00354 00355 /** 00356 * Check whether the specified directory is a valid WSGI application 00357 * root directory. 00358 * 00359 * @param mstat A CachedMultiFileStat object, if you want to use cached statting. 00360 * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL. 00361 * @throws FileSystemException Unable to check because of a filesystem error. 00362 * @ingroup Support 00363 */ 00364 bool verifyWSGIDir(const string &dir, CachedMultiFileStat *mstat = 0, 00365 unsigned int throttleRate = 0); 00366 00367 /** 00368 * Represents a buffered upload file. 00369 * 00370 * @ingroup Support 00371 */ 00372 class BufferedUpload { 00373 public: 00374 /** The file handle. */ 00375 FILE *handle; 00376 00377 /** 00378 * Create an empty upload bufer file, and open it for reading and writing. 00379 * 00380 * @throws SystemException Something went wrong. 00381 */ 00382 BufferedUpload(const char *identifier = "temp") { 00383 char templ[PATH_MAX]; 00384 int fd; 00385 00386 snprintf(templ, sizeof(templ), "%s/%s.XXXXXX", getDir().c_str(), identifier); 00387 templ[sizeof(templ) - 1] = '\0'; 00388 fd = mkstemp(templ); 00389 if (fd == -1) { 00390 char message[1024]; 00391 int e = errno; 00392 00393 snprintf(message, sizeof(message), "Cannot create a temporary file '%s'", templ); 00394 message[sizeof(message) - 1] = '\0'; 00395 throw SystemException(message, e); 00396 } 00397 00398 /* We use a POSIX trick here: the file's permissions are set to "u=,g=,o=" 00399 * and the file is deleted immediately from the filesystem, while we 00400 * keep its file handle open. The result is that no other processes 00401 * will be able to access this file's contents anymore, except us. 00402 * We now have an anonymous disk-backed buffer. 00403 */ 00404 fchmod(fd, 0000); 00405 unlink(templ); 00406 00407 handle = fdopen(fd, "w+"); 00408 } 00409 00410 ~BufferedUpload() { 00411 fclose(handle); 00412 } 00413 00414 /** 00415 * Returns the directory in which upload buffer files are stored. 00416 * This is a subdirectory of the directory returned by getPassengerTempDir(). 00417 */ 00418 static string getDir() { 00419 return getPassengerTempDir() + "/webserver_private"; 00420 } 00421 }; 00422 00423 } // namespace Passenger 00424 00425 #endif /* _PASSENGER_UTILS_H_ */ 00426