- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# ServerInstanceDir.h

    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_SERVER_INSTANCE_DIR_H_
    00026 #define _PASSENGER_SERVER_INSTANCE_DIR_H_
    00027
    00028 #include <boost/noncopyable.hpp>
    00029 #include <boost/shared_ptr.hpp>
    00030 #include <oxt/backtrace.hpp>
    00031
    00032 #include <sys/types.h>
    00033 #include <dirent.h>
    00034 #include <unistd.h>
    00035 #include <pwd.h>
    00036 #include <grp.h>
    00037 #include <cstdlib>
    00038 #include <cstring>
    00039 #include <string>
    00040
    00041 #include "Exceptions.h"
    00042 #include "Utils.h"
    00043 #include "Utils/StrIntUtils.h"
    00044
    00045 namespace Passenger {
    00046
    00047 using namespace std;
    00048 using namespace boost;
    00049
    00050 class ServerInstanceDir: public noncopyable {
    00051 public:
    00052         // Don't forget to update lib/phusion_passenger/admin_tools/server_instance.rb too.
    00053         static const int DIR_STRUCTURE_MAJOR_VERSION = 1;
    00054         static const int DIR_STRUCTURE_MINOR_VERSION = 0;
    00055         static const int GENERATION_STRUCTURE_MAJOR_VERSION = 1;
    00056         static const int GENERATION_STRUCTURE_MINOR_VERSION = 0;
    00057
    00058         class Generation: public noncopyable {
    00059         private:
    00060                 friend class ServerInstanceDir;
    00061
    00062                 string path;
    00063                 unsigned int number;
    00064                 bool owner;
    00065
    00066                 Generation(const string &serverInstanceDir, unsigned int number) {
    00067                         path = serverInstanceDir + "/generation-" + toString(number);
    00068                         this->number = number;
    00069                         owner = false;
    00070                 }
    00071
    00072                 void create(bool userSwitching, const string &defaultUser,
    00073                             const string &defaultGroup, uid_t webServerWorkerUid,
    00074                             gid_t webServerWorkerGid)
    00075                 {
    00076                         TRACE_POINT();
    00077                         bool runningAsRoot = geteuid() == 0;
    00078                         struct passwd *defaultUserEntry;
    00079                         struct group  *defaultGroupEntry;
    00080                         uid_t defaultUid;
    00081                         gid_t defaultGid;
    00082
    00083                         defaultUserEntry = getpwnam(defaultUser.c_str());
    00084                         if (defaultUserEntry == NULL) {
    00085                                 throw NonExistentUserException("Default user '" + defaultUser +
    00086                                         "' does not exist.");
    00087                         }
    00088                         defaultUid = defaultUserEntry->pw_uid;
    00089                         defaultGroupEntry = getgrnam(defaultGroup.c_str());
    00090                         if (defaultGroupEntry == NULL) {
    00091                                 throw NonExistentGroupException("Default group '" + defaultGroup +
    00092                                         "' does not exist.");
    00093                         }
    00094                         defaultGid = defaultGroupEntry->gr_gid;
    00095
    00096                         /* We set a very tight permission here: no read or write access for
    00097                          * anybody except the owner. The individual files and subdirectories
    00098                          * decide for themselves whether they're readable by anybody.
    00099                          */
    00100                         makeDirTree(path, "u=rwxs,g=x,o=x");
    00101
    00102                         /* Write structure version file. */
    00103                         string structureVersionFile = path + "/structure_version.txt";
    00104                         createFile(structureVersionFile,
    00105                                 toString(GENERATION_STRUCTURE_MAJOR_VERSION) + "." +
    00106                                 toString(GENERATION_STRUCTURE_MINOR_VERSION),
    00107                                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    00108
    00109
    00110                         /* We want the upload buffer directory to be only writable by the web
    00111                          * server's worker processs. Other users may not have any access to this
    00112                          * directory.
    00113                          */
    00114                         if (runningAsRoot) {
    00115                                 makeDirTree(path + "/buffered_uploads", "u=rwxs,g=,o=",
    00116                                         webServerWorkerUid, webServerWorkerGid);
    00117                         } else {
    00118                                 makeDirTree(path + "/buffered_uploads", "u=rwxs,g=,o=");
    00119                         }
    00120
    00121                         /* The web server must be able to directly connect to a backend. */
    00122                         if (runningAsRoot) {
    00123                                 if (userSwitching) {
    00124                                         /* Each backend process may be running as a different user,
    00125                                          * so the backends subdirectory must be world-writable.
    00126                                          * However we don't want everybody to be able to know the
    00127                                          * sockets' filenames, so the directory is not readable.
    00128                                          */
    00129                                         makeDirTree(path + "/backends", "u=rwxs,g=wx,o=wx");
    00130                                 } else {
    00131                                         /* All backend processes are running as defaultUser/defaultGroup,
    00132                                          * so make defaultUser/defaultGroup the owner and group of the
    00133                                          * subdirecory.
    00134                                          *
    00135                                          * The directory is not readable as a security precaution:
    00136                                          * nobody should be able to know the sockets' filenames without
    00137                                          * having access to the application pool.
    00138                                          */
    00139                                         makeDirTree(path + "/backends", "u=rwxs,g=x,o=x", defaultUid, defaultGid);
    00140                                 }
    00141                         } else {
    00142                                 /* All backend processes are running as the same user as the web server,
    00143                                  * so only allow access for this user.
    00144                                  */
    00145                                 makeDirTree(path + "/backends", "u=rwxs,g=,o=");
    00146                         }
    00147
    00148                         /* The helper server (containing the application pool) must be able to access
    00149                          * the spawn server's socket.
    00150                          */
    00151                         if (runningAsRoot) {
    00152                                 if (userSwitching) {
    00153                                         /* Both the helper server and the spawn server are
    00154                                          * running as root.
    00155                                          */
    00156                                         makeDirTree(path + "/spawn-server", "u=rwxs,g=,o=");
    00157                                 } else {
    00158                                         /* Both the helper server and the spawn server are
    00159                                          * running as defaultUser/defaultGroup.
    00160                                          */
    00161                                         makeDirTree(path + "/spawn-server", "u=rwxs,g=,o=",
    00162                                                 defaultUid, defaultGid);
    00163                                 }
    00164                         } else {
    00165                                 makeDirTree(path + "/spawn-server", "u=rwxs,g=,o=");
    00166                         }
    00167
    00168                         owner = true;
    00169                 }
    00170
    00171         public:
    00172                 ~Generation() {
    00173                         if (owner) {
    00174                                 removeDirTree(path);
    00175                         }
    00176                 }
    00177
    00178                 unsigned int getNumber() const {
    00179                         return number;
    00180                 }
    00181
    00182                 string getPath() const {
    00183                         return path;
    00184                 }
    00185
    00186                 void detach() {
    00187                         owner = false;
    00188                 }
    00189         };
    00190
    00191         typedef shared_ptr<Generation> GenerationPtr;
    00192
    00193 private:
    00194         string path;
    00195         bool owner;
    00196
    00197         friend class Generation;
    00198
    00199         void initialize(const string &path, bool owner) {
    00200                 TRACE_POINT();
    00201                 this->path  = path;
    00202                 this->owner = owner;
    00203
    00204                 /* Create the server instance directory. We only need to write to this
    00205                  * directory for these reasons:
    00206                  * 1. Initial population of structure files (structure_version.txt, instance.pid).
    00207                  * 2. Creating/removing a generation directory.
    00208                  * 3. Removing the entire server instance directory (after all
    00209                  *    generations are removed).
    00210                  *
    00211                  * 1 and 2 are done by the helper server during initialization and before lowering
    00212                  * privilege. 3 is done during helper server shutdown by a cleanup process that's
    00213                  * running as the same user the helper server was running as before privilege
    00214                  * lowering.
    00215                  * Therefore, we make the directory only writable by the user the helper server
    00216                  * was running as before privilege is lowered. Everybody else has read and execute
    00217                  * rights though, because we want admin tools to be able to list the available
    00218                  * generations no matter what user they're running as.
    00219                  */
    00220                 makeDirTree(path, "u=rwxs,g=rx,o=rx");
    00221         }
    00222
    00223         bool isDirectory(const string &dir, struct dirent *entry) const {
    00224                 #ifdef DT_DIR
    00225                         if (entry->d_type == DT_DIR) {
    00226                                 return true;
    00227                         } else if (entry->d_type != DT_UNKNOWN) {
    00228                                 return false;
    00229                         }
    00230                         // If DT_UNKNOWN, use normal check.
    00231                 #endif
    00232                 string path = dir;
    00233                 path.append("/");
    00234                 path.append(entry->d_name);
    00235                 return getFileType(path) == FT_DIRECTORY;
    00236         }
    00237
    00238 public:
    00239         ServerInstanceDir(pid_t webServerPid, const string &parentDir = "", bool owner = true) {
    00240                 string theParentDir;
    00241
    00242                 if (parentDir.empty()) {
    00243                         theParentDir = getSystemTempDir();
    00244                 } else {
    00245                         theParentDir = parentDir;
    00246                 }
    00247
    00248                 /* We embed the super structure version in the server instance directory name
    00249                  * because it's possible to upgrade Phusion Passenger without changing the
    00250                  * web server's PID. This way each incompatible upgrade will use its own
    00251                  * server instance directory.
    00252                  */
    00253                 initialize(theParentDir + "/passenger." +
    00254                         toString(DIR_STRUCTURE_MAJOR_VERSION) + "." +
    00255                         toString(DIR_STRUCTURE_MINOR_VERSION) + "." +
    00256                         toString<unsigned long long>(webServerPid),
    00257                         owner);
    00258
    00259         }
    00260
    00261         ServerInstanceDir(const string &path, bool owner = true) {
    00262                 initialize(path, owner);
    00263         }
    00264
    00265         ~ServerInstanceDir() {
    00266                 if (owner) {
    00267                         GenerationPtr newestGeneration;
    00268                         try {
    00269                                 newestGeneration = getNewestGeneration();
    00270                         } catch (const FileSystemException &e) {
    00271                                 if (e.code() == ENOENT) {
    00272                                         return;
    00273                                 } else {
    00274                                         throw;
    00275                                 }
    00276                         }
    00277                         if (newestGeneration == NULL) {
    00278                                 removeDirTree(path);
    00279                         }
    00280                 }
    00281         }
    00282
    00283         string getPath() const {
    00284                 return path;
    00285         }
    00286
    00287         void detach() {
    00288                 owner = false;
    00289         }
    00290
    00291         GenerationPtr newGeneration(bool userSwitching, const string &defaultUser,
    00292                                     const string &defaultGroup, uid_t webServerWorkerUid,
    00293                                     gid_t webServerWorkerGid)
    00294         {
    00295                 GenerationPtr newestGeneration = getNewestGeneration();
    00296                 unsigned int newNumber;
    00297                 if (newestGeneration != NULL) {
    00298                         newNumber = newestGeneration->getNumber() + 1;
    00299                 } else {
    00300                         newNumber = 0;
    00301                 }
    00302
    00303                 GenerationPtr generation(new Generation(path, newNumber));
    00304                 generation->create(userSwitching, defaultUser, defaultGroup,
    00305                         webServerWorkerUid, webServerWorkerGid);
    00306                 return generation;
    00307         }
    00308
    00309         GenerationPtr getGeneration(unsigned int number) const {
    00310                 return ptr(new Generation(path, number));
    00311         }
    00312
    00313         GenerationPtr getNewestGeneration() const {
    00314                 DIR *dir = opendir(path.c_str());
    00315                 struct dirent *entry;
    00316                 int result = -1;
    00317
    00318                 if (dir == NULL) {
    00319                         int e = errno;
    00320                         throw FileSystemException("Cannot open directory " + path,
    00321                                 e, path);
    00322                 }
    00323                 while ((entry = readdir(dir)) != NULL) {
    00324                         if (isDirectory(path, entry)
    00325                          && strncmp(entry->d_name, "generation-", sizeof("generation-") - 1) == 0) {
    00326                                 const char *numberString = entry->d_name + sizeof("generation-") - 1;
    00327                                 int number = atoi(numberString);
    00328                                 if (number >= 0 && number > result) {
    00329                                         result = number;
    00330                                 }
    00331                         }
    00332                 }
    00333                 closedir(dir);
    00334
    00335                 if (result == -1) {
    00336                         return GenerationPtr();
    00337                 } else {
    00338                         return getGeneration(result);
    00339                 }
    00340         }
    00341 };
    00342
    00343 typedef shared_ptr<ServerInstanceDir> ServerInstanceDirPtr;
    00344
    00345 } // namespace Passenger
    00346
    00347 #endif /* _PASSENGER_SERVER_INSTANCE_DIR_H_ */