Passenger: Utils.h Source File

- Main Page
- Modules
- Classes
- Files

- File List

# Utils.h

    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  * If <em>path</em> refers to a symlink, then this function resolves the
    00215  * symlink for 1 level. That is, if the symlink points to another symlink,
    00216  * then the other symlink will not be resolved. The resolved path is returned.
    00217  *
    00218  * If the symlink doesn't point to an absolute path, then this function will
    00219  * prepend <em>path</em>'s directory to the result.
    00220  *
    00221  * If <em>path</em> doesn't refer to a symlink then this method will return
    00222  * <em>path</em>.
    00223  *
    00224  * @throws FileSystemException Something went wrong.
    00225  * @ingroup Support
    00226  */
    00227 string resolveSymlink(const string &path);
    00228
    00229 /**
    00230  * Given a path, extracts its directory name.
    00231  *
    00232  * @ingroup Support
    00233  */
    00234 string extractDirName(const string &path);
    00235
    00236 /**
    00237  * Escape the given raw string into an XML value.
    00238  *
    00239  * @throws std::bad_alloc Something went wrong.
    00240  * @ingroup Support
    00241  */
    00242 string escapeForXml(const string &input);
    00243
    00244 /**
    00245  * Given a username that's supposed to be the "lowest user" in the user switching mechanism,
    00246  * checks whether this username exists. If so, this users's UID and GID will be stored into
    00247  * the arguments of the same names. If not, <em>uid</em> and <em>gid</em> will be set to
    00248  * the UID and GID of the "nobody" user. If that user doesn't exist either, then <em>uid</em>
    00249  * and <em>gid</em> will be set to -1.
    00250  */
    00251 void determineLowestUserAndGroup(const string &user, uid_t &uid, gid_t &gid);
    00252
    00253 /**
    00254  * Return the path name for the directory in which the system stores general
    00255  * temporary files. This is usually "/tmp", but might be something else depending
    00256  * on some environment variables.
    00257  *
    00258  * @ensure result != NULL
    00259  * @ingroup Support
    00260  */
    00261 const char *getSystemTempDir();
    00262
    00263 /**
    00264  * Return the path name for the directory in which Phusion Passenger-specific
    00265  * temporary files are to be stored. This directory is unique for this instance
    00266  * of the web server in which Phusion Passenger is running.
    00267  *
    00268  * If the environment variable PASSENGER_INSTANCE_TEMP_DIR is set, then that value
    00269  * will be returned. If this environment variable is not set, then it will be set
    00270  * with the return value.
    00271  *
    00272  * To bypass the usage of the PASSENGER_INSTANCE_TEMP_DIR environment variable,
    00273  * set 'bypassCache' to true.
    00274  *
    00275  * @param bypassCache Whether PASSENGER_INSTANCE_TEMP_DIR should be bypassed.
    00276  * @param systemTempDir The directory under which the Phusion Passenger-specific
    00277  *                      temp directory should be located. If set to the empty string,
    00278  *                      then the return value of getSystemTempDir() will be used.
    00279  * @ensure !result.empty()
    00280  * @ingroup Support
    00281  */
    00282 string getPassengerTempDir(bool bypassCache = false, const string &systemTempDir = "");
    00283
    00284 /* Create a temp directory under <em>systemTempDir</em>, for storing Phusion
    00285  * Passenger-specific temp files, such as temporarily buffered uploads,
    00286  * sockets for backend processes, etc. This call also sets the
    00287  * PASSENGER_INSTANCE_TEMP_DIR environment variable, which allows subprocesses
    00288  * to find this temp directory.
    00289  *
    00290  * The created temp directory will have several subdirectories:
    00291  * - webserver_private - for storing the web server's buffered uploads.
    00292  * - info - for storing files that allow external tools to query information
    00293  *          about a running Phusion Passenger instance.
    00294  * - backends - for storing Unix sockets created by backend processes.
    00295  * - var - for storing all other kinds of temp files that the backend processes
    00296  *         create.
    00297  *
    00298  * If a (sub)directory already exists, then it will not result in an error.
    00299  *
    00300  * The <em>userSwitching</em> and <em>lowestUser</em> arguments passed to
    00301  * this method are used for determining the optimal permissions for the
    00302  * (sub)directories. The permissions will be set as tightly as possible based
    00303  * on the values. The <em>workerUid</em> and <em>workerGid</em> arguments
    00304  * will be used for determining the owner of certain subdirectories.
    00305  *
    00306  * @note You should only call this method inside the web server's master
    00307  *       process. In case of Apache, this is the Apache control process,
    00308  *       the one that tends to run as root. This is because this function
    00309  *       will set directory permissions and owners/groups, which may require
    00310  *       root privileges.
    00311  *
    00312  * @param systemTempDir The directory under which the Phusion Passenger-specific
    00313  *                      temp directory should be created. You should normally
    00314  *                      specify the return value of getSystemTempDir().
    00315  * @param userSwitching Whether user switching is turned on.
    00316  * @param lowestUser The user that the spawn manager and the pool server will
    00317  *                   run as, if user switching is turned off.
    00318  * @param workerUid The UID that the web server's worker processes are running
    00319  *                  as. On Apache, this is the UID that's associated with the
    00320  *                  'User' directive.
    00321  * @param workerGid The GID that the web server's worker processes are running
    00322  *                  as. On Apache, this is the GID that's associated with the
    00323  *                  'Group' directive.
    00324  * @throws IOException Something went wrong.
    00325  * @throws SystemException Something went wrong.
    00326  * @throws FileSystemException Something went wrong.
    00327  */
    00328 void createPassengerTempDir(const string &systemTempDir, bool userSwitching,
    00329                             const string &lowestUser,
    00330                             uid_t workerUid, gid_t workerGid);
    00331
    00332 /**
    00333  * Create the directory at the given path, creating intermediate directories
    00334  * if necessary. The created directories' permissions are as specified by the
    00335  * 'mode' parameter. You can specify this directory's owner and group through
    00336  * the 'owner' and 'group' parameters. A value of -1 for 'owner' or 'group'
    00337  * means that the owner/group should not be changed.
    00338  *
    00339  * If 'path' already exists, then nothing will happen.
    00340  *
    00341  * @throws IOException Something went wrong.
    00342  * @throws SystemException Something went wrong.
    00343  * @throws FileSystemException Something went wrong.
    00344  */
    00345 void makeDirTree(const string &path, const char *mode = "u=rwx,g=,o=", uid_t owner = -1, gid_t group = -1);
    00346
    00347 /**
    00348  * Remove an entire directory tree recursively.
    00349  *
    00350  * @throws FileSystemException Something went wrong.
    00351  */
    00352 void removeDirTree(const string &path);
    00353
    00354 /**
    00355  * Check whether the specified directory is a valid Ruby on Rails
    00356  * application root directory.
    00357  *
    00358  * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
    00359  * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
    00360  * @throws FileSystemException Unable to check because of a system error.
    00361  * @ingroup Support
    00362  */
    00363 bool verifyRailsDir(const string &dir, CachedMultiFileStat *mstat = 0,
    00364                     unsigned int throttleRate = 0);
    00365
    00366 /**
    00367  * Check whether the specified directory is a valid Rack application
    00368  * root directory.
    00369  *
    00370  * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
    00371  * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
    00372  * @throws FileSystemException Unable to check because of a filesystem error.
    00373  * @ingroup Support
    00374  */
    00375 bool verifyRackDir(const string &dir, CachedMultiFileStat *mstat = 0,
    00376                    unsigned int throttleRate = 0);
    00377
    00378 /**
    00379  * Check whether the specified directory is a valid WSGI application
    00380  * root directory.
    00381  *
    00382  * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
    00383  * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
    00384  * @throws FileSystemException Unable to check because of a filesystem error.
    00385  * @ingroup Support
    00386  */
    00387 bool verifyWSGIDir(const string &dir, CachedMultiFileStat *mstat = 0,
    00388                    unsigned int throttleRate = 0);
    00389
    00390 /**
    00391  * Represents a buffered upload file.
    00392  *
    00393  * @ingroup Support
    00394  */
    00395 class BufferedUpload {
    00396 public:
    00397         /** The file handle. */
    00398         FILE *handle;
    00399
    00400         /**
    00401          * Create an empty upload bufer file, and open it for reading and writing.
    00402          *
    00403          * @throws SystemException Something went wrong.
    00404          */
    00405         BufferedUpload(const char *identifier = "temp") {
    00406                 char templ[PATH_MAX];
    00407                 int fd;
    00408
    00409                 snprintf(templ, sizeof(templ), "%s/%s.XXXXXX", getDir().c_str(), identifier);
    00410                 templ[sizeof(templ) - 1] = '\0';
    00411                 fd = mkstemp(templ);
    00412                 if (fd == -1) {
    00413                         char message[1024];
    00414                         int e = errno;
    00415
    00416                         snprintf(message, sizeof(message), "Cannot create a temporary file '%s'", templ);
    00417                         message[sizeof(message) - 1] = '\0';
    00418                         throw SystemException(message, e);
    00419                 }
    00420
    00421                 /* We use a POSIX trick here: the file's permissions are set to "u=,g=,o="
    00422                  * and the file is deleted immediately from the filesystem, while we
    00423                  * keep its file handle open. The result is that no other processes
    00424                  * will be able to access this file's contents anymore, except us.
    00425                  * We now have an anonymous disk-backed buffer.
    00426                  */
    00427                 fchmod(fd, 0000);
    00428                 unlink(templ);
    00429
    00430                 handle = fdopen(fd, "w+");
    00431         }
    00432
    00433         ~BufferedUpload() {
    00434                 fclose(handle);
    00435         }
    00436
    00437         /**
    00438          * Returns the directory in which upload buffer files are stored.
    00439          * This is a subdirectory of the directory returned by getPassengerTempDir().
    00440          */
    00441         static string getDir() {
    00442                 return getPassengerTempDir() + "/webserver_private";
    00443         }
    00444 };
    00445
    00446 } // namespace Passenger
    00447
    00448 #endif /* _PASSENGER_UTILS_H_ */
    00449