00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
00097
00098
00099
00100 makeDirTree(path, "u=rwxs,g=x,o=x");
00101
00102
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
00111
00112
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
00122 if (runningAsRoot) {
00123 if (userSwitching) {
00124
00125
00126
00127
00128
00129 makeDirTree(path + "/backends", "u=rwxs,g=wx,o=wx");
00130 } else {
00131
00132
00133
00134
00135
00136
00137
00138
00139 makeDirTree(path + "/backends", "u=rwxs,g=x,o=x", defaultUid, defaultGid);
00140 }
00141 } else {
00142
00143
00144
00145 makeDirTree(path + "/backends", "u=rwxs,g=,o=");
00146 }
00147
00148
00149
00150
00151 if (runningAsRoot) {
00152 if (userSwitching) {
00153
00154
00155
00156 makeDirTree(path + "/spawn-server", "u=rwxs,g=,o=");
00157 } else {
00158
00159
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
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
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
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
00249
00250
00251
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 }
00346
00347 #endif