- Main Page
- Modules
- Classes
- Files

# 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  * 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