00001 /* 00002 * Phusion Passenger - http://www.modrails.com/ 00003 * Copyright (c) 2010 Phusion 00004 * 00005 * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. 00006 * 00007 * Permission is hereby granted, free of charge, to any person obtaining a copy 00008 * of this software and associated documentation files (the "Software"), to deal 00009 * in the Software without restriction, including without limitation the rights 00010 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00011 * copies of the Software, and to permit persons to whom the Software is 00012 * furnished to do so, subject to the following conditions: 00013 * 00014 * The above copyright notice and this permission notice shall be included in 00015 * all copies or substantial portions of the Software. 00016 * 00017 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00020 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00022 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00023 * THE SOFTWARE. 00024 */ 00025 #ifndef _PASSENGER_UTILS_H_ 00026 #define _PASSENGER_UTILS_H_ 00027 00028 #include <boost/shared_ptr.hpp> 00029 #include <sys/types.h> 00030 #include <sys/stat.h> 00031 #include <string> 00032 #include <vector> 00033 #include <utility> 00034 #include <sstream> 00035 #include <cstdio> 00036 #include <climits> 00037 #include <cstdlib> 00038 #include <cstring> 00039 #include <errno.h> 00040 #include <unistd.h> 00041 #include "StaticString.h" 00042 #include "Exceptions.h" 00043 00044 namespace Passenger { 00045 00046 using namespace std; 00047 using namespace boost; 00048 00049 static const uid_t USER_NOT_GIVEN = (uid_t) -1; 00050 static const gid_t GROUP_NOT_GIVEN = (gid_t) -1; 00051 00052 typedef struct CachedFileStat CachedFileStat; 00053 class ResourceLocator; 00054 00055 /** Enumeration which indicates what kind of file a file is. */ 00056 typedef enum { 00057 /** The file doesn't exist. */ 00058 FT_NONEXISTANT, 00059 /** A regular file or a symlink to a regular file. */ 00060 FT_REGULAR, 00061 /** A directory. */ 00062 FT_DIRECTORY, 00063 /** Something else, e.g. a pipe or a socket. */ 00064 FT_OTHER 00065 } FileType; 00066 00067 /** 00068 * Convenience shortcut for creating a <tt>shared_ptr</tt>. 00069 * Instead of: 00070 * @code 00071 * shared_ptr<Foo> foo; 00072 * ... 00073 * foo = shared_ptr<Foo>(new Foo()); 00074 * @endcode 00075 * one can write: 00076 * @code 00077 * shared_ptr<Foo> foo; 00078 * ... 00079 * foo = ptr(new Foo()); 00080 * @endcode 00081 * 00082 * @param pointer The item to put in the shared_ptr object. 00083 * @ingroup Support 00084 */ 00085 template<typename T> shared_ptr<T> 00086 ptr(T *pointer) { 00087 return shared_ptr<T>(pointer); 00088 } 00089 00090 /** 00091 * Used internally by toString(). Do not use directly. 00092 * 00093 * @internal 00094 */ 00095 template<typename T> 00096 struct AnythingToString { 00097 string operator()(T something) { 00098 stringstream s; 00099 s << something; 00100 return s.str(); 00101 } 00102 }; 00103 00104 /** 00105 * Used internally by toString(). Do not use directly. 00106 * 00107 * @internal 00108 */ 00109 template<> 00110 struct AnythingToString< vector<string> > { 00111 string operator()(const vector<string> &v) { 00112 string result("["); 00113 vector<string>::const_iterator it; 00114 unsigned int i; 00115 for (it = v.begin(), i = 0; it != v.end(); it++, i++) { 00116 result.append("'"); 00117 result.append(*it); 00118 if (i == v.size() - 1) { 00119 result.append("'"); 00120 } else { 00121 result.append("', "); 00122 } 00123 } 00124 result.append("]"); 00125 return result; 00126 } 00127 }; 00128 00129 /** 00130 * Convert anything to a string. 00131 * 00132 * @param something The thing to convert. 00133 * @ingroup Support 00134 */ 00135 template<typename T> string 00136 toString(T something) { 00137 return AnythingToString<T>()(something); 00138 } 00139 00140 /** 00141 * Converts the given string to an integer. 00142 * @ingroup Support 00143 */ 00144 int atoi(const string &s); 00145 00146 /** 00147 * Converts the given string to a long integer. 00148 * @ingroup Support 00149 */ 00150 long atol(const string &s); 00151 00152 /** Round <em>number</em> up to the nearest multiple of <em>multiple</em>. */ 00153 template<typename IntType> IntType 00154 roundUp(IntType number, IntType multiple) { 00155 return (number + multiple - 1) / multiple * multiple; 00156 } 00157 00158 /** 00159 * Split the given string using the given separator. 00160 * 00161 * @param str The string to split. 00162 * @param sep The separator to use. 00163 * @param output The vector to write the output to. 00164 * @ingroup Support 00165 */ 00166 void split(const string &str, char sep, vector<string> &output); 00167 00168 /** 00169 * Check whether the specified file exists. 00170 * 00171 * @param filename The filename to check. 00172 * @param cstat A CachedFileStat object, if you want to use cached statting. 00173 * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. 00174 * @return Whether the file exists. 00175 * @throws FileSystemException Unable to check because of a filesystem error. 00176 * @throws TimeRetrievalException 00177 * @throws boost::thread_interrupted 00178 * @ingroup Support 00179 */ 00180 bool fileExists(const StaticString &filename, CachedFileStat *cstat = 0, 00181 unsigned int throttleRate = 0); 00182 00183 /** 00184 * Check whether 'filename' exists and what kind of file it is. 00185 * 00186 * @param filename The filename to check. 00187 * @param mstat A CachedFileStat object, if you want to use cached statting. 00188 * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. 00189 * @return The file type. 00190 * @throws FileSystemException Unable to check because of a filesystem error. 00191 * @throws TimeRetrievalException 00192 * @throws boost::thread_interrupted 00193 * @ingroup Support 00194 */ 00195 FileType getFileType(const StaticString &filename, CachedFileStat *cstat = 0, 00196 unsigned int throttleRate = 0); 00197 00198 /** 00199 * Create the given file with the given contents, permissions and ownership. 00200 * This function does not leave behind junk files: if the ownership cannot be set 00201 * or if not all data can be written then then the file will be deleted. 00202 * 00203 * @param filename The file to create. 00204 * @param contents The contents to write to the file. 00205 * @param permissions The desired file permissions. 00206 * @param owner The desired file owner. Specify USER_NOT_GIVEN if you want to use the current 00207 * process's owner as the file owner. 00208 * @param group The desired file group. Specify GROUP_NOT_GIVEN if you want to use the current 00209 * process's group as the file group. 00210 * @param overwrite Whether to overwrite the file if it exists. If set to false 00211 * and the file exists then nothing will happen. 00212 * @throws FileSystemException Something went wrong. 00213 * @ingroup Support 00214 */ 00215 void createFile(const string &filename, const StaticString &contents, 00216 mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 00217 uid_t owner = USER_NOT_GIVEN, gid_t group = GROUP_NOT_GIVEN, 00218 bool overwrite = true); 00219 00220 /** 00221 * Returns a canonical version of the specified path. All symbolic links 00222 * and relative path elements are resolved. 00223 * 00224 * @throws FileSystemException Something went wrong. 00225 * @ingroup Support 00226 */ 00227 string canonicalizePath(const string &path); 00228 00229 /** 00230 * If <em>path</em> refers to a symlink, then this function resolves the 00231 * symlink for 1 level. That is, if the symlink points to another symlink, 00232 * then the other symlink will not be resolved. The resolved path is returned. 00233 * 00234 * If the symlink doesn't point to an absolute path, then this function will 00235 * prepend <em>path</em>'s directory to the result. 00236 * 00237 * If <em>path</em> doesn't refer to a symlink then this method will return 00238 * <em>path</em>. 00239 * 00240 * @throws FileSystemException Something went wrong. 00241 * @ingroup Support 00242 */ 00243 string resolveSymlink(const string &path); 00244 00245 /** 00246 * Given a path, extracts its directory name. 00247 * 00248 * @ingroup Support 00249 */ 00250 string extractDirName(const StaticString &path); 00251 00252 /** 00253 * Given a path, extracts its base name. 00254 * 00255 * @ingroup Support 00256 */ 00257 string extractBaseName(const StaticString &path); 00258 00259 /** 00260 * Escape the given raw string into an XML value. 00261 * 00262 * @throws std::bad_alloc Something went wrong. 00263 * @ingroup Support 00264 */ 00265 string escapeForXml(const string &input); 00266 00267 /** 00268 * Returns the username of the user that the current process is running as. 00269 * If the user has no associated username, then "UID xxxx" is returned, 00270 * where xxxx is the current UID. 00271 */ 00272 string getProcessUsername(); 00273 00274 /** 00275 * Converts a mode string into a mode_t value. 00276 * 00277 * At this time only the symbolic mode strings are supported, e.g. something like looks 00278 * this: "u=rwx,g=w,o=rx". The grammar is as follows: 00279 * @code 00280 * mode ::= (clause ("," clause)*)? 00281 * clause ::= who "=" permission* 00282 * who ::= "u" | "g" | "o" 00283 * permission ::= "r" | "w" | "x" | "s" 00284 * @endcode 00285 * 00286 * Notes: 00287 * - The mode value starts with 0. So if you specify "u=rwx", then the group and world 00288 * permissions will be empty (set to 0). 00289 * - The "s" permission is only allowed for who == "u" or who == "g". 00290 * - The return value does not depend on the umask. 00291 * 00292 * @throws InvalidModeStringException The mode string cannot be parsed. 00293 */ 00294 mode_t parseModeString(const StaticString &mode); 00295 00296 /** 00297 * Return the path name for the directory in which the system stores general 00298 * temporary files. This is usually "/tmp", but might be something else depending 00299 * on some environment variables. 00300 * 00301 * @ensure result != NULL 00302 * @ingroup Support 00303 */ 00304 const char *getSystemTempDir(); 00305 00306 /* Create a temporary directory for storing Phusion Passenger instance-specific 00307 * temp files, such as temporarily buffered uploads, sockets for backend 00308 * processes, etc. 00309 * The directory that will be created is the one returned by 00310 * <tt>getPassengerTempDir(false, parentDir)</tt>. This call stores the path to 00311 * this temp directory in an internal variable, so that subsequent calls to 00312 * getPassengerTempDir() will return the same path. 00313 * 00314 * The created temp directory will have several subdirectories: 00315 * - webserver_private - for storing the web server's buffered uploads. 00316 * - info - for storing files that allow external tools to query information 00317 * about a running Phusion Passenger instance. 00318 * - backends - for storing Unix sockets created by backend processes. 00319 * - master - for storing files such as the Passenger HelperServer socket. 00320 * 00321 * If a (sub)directory already exists, then it will not result in an error. 00322 * 00323 * The <em>userSwitching</em> and <em>lowestUser</em> arguments passed to 00324 * this method are used for determining the optimal permissions for the 00325 * (sub)directories. The permissions will be set as tightly as possible based 00326 * on the values. The <em>workerUid</em> and <em>workerGid</em> arguments 00327 * will be used for determining the owner of certain subdirectories. 00328 * 00329 * @note You should only call this method inside the web server's master 00330 * process. In case of Apache, this is the Apache control process, 00331 * the one that tends to run as root. This is because this function 00332 * will set directory permissions and owners/groups, which may require 00333 * root privileges. 00334 * 00335 * @param parentDir The directory under which the Phusion Passenger-specific 00336 * temp directory should be created. This argument may be the 00337 * empty string, in which case getSystemTempDir() will be used 00338 * as the parent directory. 00339 * @param userSwitching Whether user switching is turned on. 00340 * @param lowestUser The user that the spawn manager and the pool server will 00341 * run as, if user switching is turned off. 00342 * @param workerUid The UID that the web server's worker processes are running 00343 * as. On Apache, this is the UID that's associated with the 00344 * 'User' directive. 00345 * @param workerGid The GID that the web server's worker processes are running 00346 * as. On Apache, this is the GID that's associated with the 00347 * 'Group' directive. 00348 * @throws IOException Something went wrong. 00349 * @throws SystemException Something went wrong. 00350 * @throws FileSystemException Something went wrong. 00351 */ 00352 /* void createPassengerTempDir(const string &parentDir, bool userSwitching, 00353 const string &lowestUser, 00354 uid_t workerUid, gid_t workerGid); */ 00355 00356 /** 00357 * Create the directory at the given path, creating intermediate directories 00358 * if necessary. The created directories' permissions are exactly as specified 00359 * by the 'mode' parameter (i.e. the umask will be ignored). You can specify 00360 * this directory's owner and group through the 'owner' and 'group' parameters. 00361 * A value of USER_NOT_GIVEN for 'owner' and/or GROUP_NOT_GIVEN 'group' means 00362 * that the owner/group should not be changed. 00363 * 00364 * If 'path' already exists, then nothing will happen. 00365 * 00366 * @param mode A mode string, as supported by parseModeString(). 00367 * @throws FileSystemException Something went wrong. 00368 * @throws InvalidModeStringException The mode string cannot be parsed. 00369 */ 00370 void makeDirTree(const string &path, const StaticString &mode = "u=rwx,g=,o=", 00371 uid_t owner = USER_NOT_GIVEN, gid_t group = GROUP_NOT_GIVEN); 00372 00373 /** 00374 * Remove an entire directory tree recursively. If the directory doesn't exist then this 00375 * function does nothing. 00376 * 00377 * @throws FileSystemException Something went wrong. 00378 */ 00379 void removeDirTree(const string &path); 00380 00381 /** 00382 * Check whether the specified directory is a valid Ruby on Rails 00383 * application root directory. 00384 * 00385 * @param cstat A CachedFileStat object, if you want to use cached statting. 00386 * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. 00387 * @throws FileSystemException Unable to check because of a system error. 00388 * @throws TimeRetrievalException 00389 * @throws boost::thread_interrupted 00390 * @ingroup Support 00391 */ 00392 bool verifyRailsDir(const string &dir, CachedFileStat *cstat = 0, 00393 unsigned int throttleRate = 0); 00394 00395 /** 00396 * Check whether the specified directory is a valid Rack application 00397 * root directory. 00398 * 00399 * @param cstat A CachedFileStat object, if you want to use cached statting. 00400 * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. 00401 * @throws FileSystemException Unable to check because of a filesystem error. 00402 * @throws TimeRetrievalException 00403 * @throws boost::thread_interrupted 00404 * @ingroup Support 00405 */ 00406 bool verifyRackDir(const string &dir, CachedFileStat *cstat = 0, 00407 unsigned int throttleRate = 0); 00408 00409 /** 00410 * Check whether the specified directory is a valid WSGI application 00411 * root directory. 00412 * 00413 * @param cstat A CachedFileStat object, if you want to use cached statting. 00414 * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. 00415 * @throws FileSystemException Unable to check because of a filesystem error. 00416 * @throws TimeRetrievalException 00417 * @throws boost::thread_interrupted 00418 * @ingroup Support 00419 */ 00420 bool verifyWSGIDir(const string &dir, CachedFileStat *cstat = 0, 00421 unsigned int throttleRate = 0); 00422 00423 void prestartWebApps(const ResourceLocator &locator, const string &serializedprestartURLs); 00424 00425 /** 00426 * Generate a secure, random token of the <tt>size</tt> bytes and put 00427 * the result into <tt>buf</tt>. 00428 * 00429 * This method is thread-safe. 00430 * 00431 * @throws FileSystemException 00432 * @throws IOException 00433 * @throws boost::thread_interrupted A system call has been interrupted. 00434 * @ingroup Support 00435 */ 00436 void generateSecureToken(void *buf, unsigned int size); 00437 00438 /** 00439 * Given a prefix string, a middle string and a postfix string, try to build a string 00440 * that looks like <tt>prefix + middle + postfix</tt>, with as many characters from 00441 * <tt>midle</tt> preserved as possible. 00442 * 00443 * If <tt>prefix + middle + postfix</tt> does not fit in <tt>max</tt> characters, 00444 * then <tt>middle</tt> will be truncated so that it fits. If <tt>max</tt> is too 00445 * small to contain even 1 character from <tt>middle</tt>, then an ArgumentException 00446 * will be thrown. 00447 * 00448 * @code 00449 * fillInMiddle(18, "server.", "1234", ".socket"); // "server.1234.socket" 00450 * fillInMiddle(16, "server.", "1234", ".socket"); // "server.12.socket" 00451 * fillInMiddle(14, "server.", "1234", ".socket"); // ArgumentException 00452 * @endcode 00453 * 00454 * @returns The resulting string, with <tt>middle</tt> possibly truncated. 00455 * @throws ArgumentException <tt>max</tt> is too small to contain even 1 00456 * character from <tt>middle</tt>. 00457 * @post result.size() <= max 00458 */ 00459 string fillInMiddle(unsigned int max, const string &prefix, const string &middle, const string &postfix = ""); 00460 00461 /** 00462 * Convert the given binary data to hexadecimal. 00463 */ 00464 string toHex(const StaticString &data); 00465 00466 /** 00467 * Convert the given binary data to hexadecimal. This form accepts an 00468 * output buffer which must be at least data.size() * 2 bytes large. 00469 */ 00470 void toHex(const StaticString &data, char *output); 00471 00472 /** 00473 * Convert a signal number to its associated name. 00474 */ 00475 string getSignalName(int sig); 00476 00477 /** 00478 * Create a new Unix server socket which is bounded to <tt>filename</tt>. 00479 * 00480 * @param filename The filename to bind the socket to. 00481 * @param backlogSize The size of the socket's backlog. Specify 0 to use the 00482 * platform's maximum allowed backlog size. 00483 * @param autoDelete Whether <tt>filename</tt> should be deleted, if it already exists. 00484 * @return The file descriptor of the newly created Unix server socket. 00485 * @throws RuntimeException Something went wrong. 00486 * @throws SystemException Something went wrong while creating the Unix server socket. 00487 * @throws boost::thread_interrupted A system call has been interrupted. 00488 * @ingroup Support 00489 */ 00490 int createUnixServer(const char *filename, unsigned int backlogSize = 0, bool autoDelete = true); 00491 00492 /** 00493 * Connect to a Unix server socket at <tt>filename</tt>. 00494 * 00495 * @param filename The filename of the socket to connect to. 00496 * @return The file descriptor of the connected client socket. 00497 * @throws RuntimeException Something went wrong. 00498 * @throws SystemException Something went wrong while connecting to the Unix server. 00499 * @throws boost::thread_interrupted A system call has been interrupted. 00500 * @ingroup Support 00501 */ 00502 int connectToUnixServer(const char *filename); 00503 00504 /** 00505 * Connect to a TCP server socket at the given host name and port. 00506 * 00507 * @param hostname The host name of the TCP server. 00508 * @param port The port number of the TCP server. 00509 * @return The file descriptor of the connected client socket. 00510 * @throws IOException Something went wrong while connecting to the Unix server. 00511 * @throws SystemException Something went wrong while connecting to the Unix server. 00512 * @throws boost::thread_interrupted A system call has been interrupted. 00513 * @ingroup Support 00514 */ 00515 int connectToTcpServer(const char *hostname, unsigned int port); 00516 00517 00518 00519 /** 00520 * Represents a buffered upload file. 00521 * 00522 * @ingroup Support 00523 */ 00524 class BufferedUpload { 00525 public: 00526 /** The file handle. */ 00527 FILE *handle; 00528 00529 /** 00530 * Create an empty upload bufer file, and open it for reading and writing. 00531 * 00532 * @throws SystemException Something went wrong. 00533 */ 00534 BufferedUpload(const string &dir, const char *identifier = "temp") { 00535 char templ[PATH_MAX]; 00536 int fd; 00537 00538 snprintf(templ, sizeof(templ), "%s/%s.XXXXXX", dir.c_str(), identifier); 00539 templ[sizeof(templ) - 1] = '\0'; 00540 fd = mkstemp(templ); 00541 if (fd == -1) { 00542 char message[1024]; 00543 int e = errno; 00544 00545 snprintf(message, sizeof(message), "Cannot create a temporary file '%s'", templ); 00546 message[sizeof(message) - 1] = '\0'; 00547 throw SystemException(message, e); 00548 } 00549 00550 /* We use a POSIX trick here: the file's permissions are set to "u=,g=,o=" 00551 * and the file is deleted immediately from the filesystem, while we 00552 * keep its file handle open. The result is that no other processes 00553 * will be able to access this file's contents anymore, except us. 00554 * We now have an anonymous disk-backed buffer. 00555 */ 00556 fchmod(fd, 0000); 00557 unlink(templ); 00558 00559 handle = fdopen(fd, "w+"); 00560 } 00561 00562 ~BufferedUpload() { 00563 fclose(handle); 00564 } 00565 }; 00566 00567 /** 00568 * Fills the given memory space or string with zeroes when a MemoryZeroGuard object 00569 * is destroyed. Useful for ensuring that buffers containing password data or 00570 * other sensitive information is cleared when it goes out of scope. 00571 */ 00572 class MemZeroGuard { 00573 private: 00574 void *data; 00575 unsigned int size; 00576 string *str; 00577 00578 static void securelyZeroMemory(volatile void *data, unsigned int size) { 00579 /* We do not use memset() here because the compiler may 00580 * optimize out memset() calls. Instead, the following 00581 * code is guaranteed to zero the memory. 00582 * http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html 00583 */ 00584 volatile char *p = (volatile char *) data; 00585 while (size--) { 00586 *p++ = 0; 00587 } 00588 } 00589 00590 public: 00591 /** 00592 * Creates a new MemZeroGuard object with a memory region to zero. 00593 * 00594 * @param data The data to zero after destruction. 00595 * @param size The size of the data. 00596 * @pre data != NULL 00597 */ 00598 MemZeroGuard(void *data, unsigned int size) { 00599 this->data = data; 00600 this->size = size; 00601 this->str = NULL; 00602 } 00603 00604 /** 00605 * Creates a new MemoryZeroGuard object with a string to zero. 00606 * 00607 * @param str The string to zero after destruction. 00608 */ 00609 MemZeroGuard(string &str) { 00610 this->data = NULL; 00611 this->size = NULL; 00612 this->str = &str; 00613 } 00614 00615 /** 00616 * Zero the data immediately. The data will still be zeroed after 00617 * destruction of this object. 00618 */ 00619 void zeroNow() { 00620 if (str == NULL) { 00621 securelyZeroMemory(data, size); 00622 } else { 00623 securelyZeroMemory((volatile void *) str->c_str(), str->size()); 00624 } 00625 } 00626 00627 ~MemZeroGuard() { 00628 zeroNow(); 00629 } 00630 }; 00631 00632 } // namespace Passenger 00633 00634 #endif /* _PASSENGER_UTILS_H_ */ 00635