Commit message: Fix low-urgency security vulnerability: writing files to arbitrary directory by hijacking temp directories.
CVE: None
Vulnerability description: None

Project: passenger
File: ServerInstanceDir.h

	void createDirectory(const string &path) const {
		// We do not use makeDirTree() here. If an attacker creates a directory
		// just before we do, then we want to abort because we want the directory
		// to have specific permissions.
		if (mkdir(path.c_str(), parseModeString("u=rwx,g=rx,o=rx")) == -1) {
			int e = errno;
			throw FileSystemException("Cannot create server instance directory '" +
				path + "'", e, path);
		}
		// Explicitly chmod the directory in case the umask is interfering.
		if (chmod(path.c_str(), parseModeString("u=rwx,g=rx,o=rx")) == -1) {
			int e = errno;
			throw FileSystemException("Cannot set permissions on server instance directory '" +
				path + "'", e, path);
		}
		// verifyDirectoryPermissions() checks for the owner/group so we must make
		// sure the server instance directory has that owner/group, even when the
		// parent directory has setgid on.
		if (chown(path.c_str(), geteuid(), getegid()) == -1) {
			int e = errno;
			throw FileSystemException("Cannot change the permissions of the server "
				"instance directory '" + path + "'", e, path);
		}
	}

Project: passenger
File: ServerInstanceDir.h

	bool isDirectory(const string &dir, struct dirent *entry) const {
		#ifdef DT_DIR
			if (entry->d_type == DT_DIR) {
				return true;
			} else if (entry->d_type != DT_UNKNOWN) {
				return false;
			}
			// If DT_UNKNOWN, use normal check.
		#endif
		string path = dir;
		path.append("/");
		path.append(entry->d_name);
		return getFileType(path) == FT_DIRECTORY;
	}

Project: passenger
File: Utils.cpp

closeAllFileDescriptors(int lastToKeepOpen) {
	#if defined(F_CLOSEM)
		int ret;
		do {
			ret = fcntl(lastToKeepOpen + 1, F_CLOSEM);
		} while (ret == -1 && errno == EINTR);
		if (ret != -1) {
			return;
		}
	#elif defined(HAS_CLOSEFROM)
		closefrom(lastToKeepOpen + 1);
		return;
	#endif
	
	for (int i = getHighestFileDescriptor(); i > lastToKeepOpen; i--) {
		/* Even though we normally shouldn't retry on EINTR
		 * (http://news.ycombinator.com/item?id=3363819)
		 * it's okay to do that here because because this function
		 * may only be called in a single-threaded environment.
		 */
		int ret;
		do {
			ret = close(i);
		} while (ret == -1 && errno == EINTR);
	}
}

Project: passenger
File: Utils.cpp

extractBaseName(const StaticString &path) {
	char *path_copy = strdup(path.c_str());
	string result_string = basename(path_copy);
	free(path_copy);
	return result_string;
}

Project: passenger
File: Utils.cpp

runShellCommand(const StaticString &command) {
	pid_t pid = fork();
	if (pid == 0) {
		resetSignalHandlersAndMask();
		disableMallocDebugging();
		closeAllFileDescriptors(2);
		execlp("/bin/sh", "/bin/sh", "-c", command.data(), (char * const) 0);
		_exit(1);
	} else if (pid == -1) {
		return -1;
	} else {
		int status;
		if (waitpid(pid, &status, 0) == -1) {
			return -1;
		} else {
			return status;
		}
	}
}

Project: passenger
File: Spawner.h

	const string &getPath() const {
		return path;
	}

Project: passenger
File: Utils.cpp

parseModeString(const StaticString &mode) {
	mode_t modeBits = 0;
	vector<string> clauses;
	vector<string>::iterator it;
	
	split(mode, ',', clauses);
	for (it = clauses.begin(); it != clauses.end(); it++) {
		const string &clause = *it;
		
		if (clause.empty()) {
			continue;
		} else if (clause.size() < 2 || (clause[0] != '+' && clause[1] != '=')) {
			throw InvalidModeStringException("Invalid mode clause specification '" + clause + "'");
		}
		
		switch (clause[0]) {
		case 'u':
			for (string::size_type i = 2; i < clause.size(); i++) {
				switch (clause[i]) {
				case 'r':
					modeBits |= S_IRUSR;
					break;
				case 'w':
					modeBits |= S_IWUSR;
					break;
				case 'x':
					modeBits |= S_IXUSR;
					break;
				case 's':
					modeBits |= S_ISUID;
					break;
				default:
					throw InvalidModeStringException("Invalid permission '" +
						string(1, clause[i]) +
						"' in mode clause specification '" +
						clause + "'");
				}
			}
			break;
		case 'g':
			for (string::size_type i = 2; i < clause.size(); i++) {
				switch (clause[i]) {
				case 'r':
					modeBits |= S_IRGRP;
					break;
				case 'w':
					modeBits |= S_IWGRP;
					break;
				case 'x':
					modeBits |= S_IXGRP;
					break;
				case 's':
					modeBits |= S_ISGID;
					break;
				default:
					throw InvalidModeStringException("Invalid permission '" +
						string(1, clause[i]) +
						"' in mode clause specification '" +
						clause + "'");
				}
			}
			break;
		case 'o':
			for (string::size_type i = 2; i < clause.size(); i++) {
				switch (clause[i]) {
				case 'r':
					modeBits |= S_IROTH;
					break;
				case 'w':
					modeBits |= S_IWOTH;
					break;
				case 'x':
					modeBits |= S_IXOTH;
					break;
				default:
					throw InvalidModeStringException("Invalid permission '" +
						string(1, clause[i]) +
						"' in mode clause specification '" +
						clause + "'");
				}
			}
			break;
		case '+':
			for (string::size_type i = 1; i < clause.size(); i++) {
				switch (clause[i]) {
				case 't':
					modeBits |= S_ISVTX;
					break;
				default:
					throw InvalidModeStringException("Invalid permission '" +
						string(1, clause[i]) +
						"' in mode clause specification '" +
						clause + "'");
				}
			}
			break;
		default:
			throw InvalidModeStringException("Invalid owner '" + string(1, clause[0]) +
				"' in mode clause specification '" + clause + "'");
		}
	}
	
	return modeBits;
}

Project: passenger
File: ServerInstanceDir.h

		void create(bool userSwitching, const string &defaultUser,
		            const string &defaultGroup, uid_t webServerWorkerUid,
		            gid_t webServerWorkerGid)
		{
			TRACE_POINT();
			bool runningAsRoot = geteuid() == 0;
			struct passwd *defaultUserEntry;
			uid_t defaultUid;
			gid_t defaultGid;
			
			defaultUserEntry = getpwnam(defaultUser.c_str());
			if (defaultUserEntry == NULL) {
				throw NonExistentUserException("Default user '" + defaultUser +
					"' does not exist.");
			}
			defaultUid = defaultUserEntry->pw_uid;
			defaultGid = lookupGid(defaultGroup);
			if (defaultGid == (gid_t) -1) {
				throw NonExistentGroupException("Default group '" + defaultGroup +
					"' does not exist.");
			}
			
			/* We set a very tight permission here: no read or write access for
			 * anybody except the owner. The individual files and subdirectories
			 * decide for themselves whether they're readable by anybody.
			 */
			makeDirTree(path, "u=rwx,g=x,o=x");
			
			/* Write structure version file. */
			string structureVersionFile = path + "/structure_version.txt";
			createFile(structureVersionFile,
				toString(SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MAJOR_VERSION) + "." +
				toString(SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MINOR_VERSION),
				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
			
			string passengerVersionFile = path + "/passenger_version.txt";
			createFile(passengerVersionFile,
				PASSENGER_VERSION "\n",
				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
			
			
			/* We want the upload buffer directory to be only writable by the web
			 * server's worker processs. Other users may not have any access to this
			 * directory.
			 */
			if (runningAsRoot) {
				makeDirTree(path + "/buffered_uploads", "u=rwx,g=,o=",
					webServerWorkerUid, webServerWorkerGid);
			} else {
				makeDirTree(path + "/buffered_uploads", "u=rwx,g=,o=");
			}
			
			/* The HelperAgent must be able to connect to an application. */
			if (runningAsRoot) {
				if (userSwitching) {
					/* Each application process may be running as a different user,
					 * so the backends subdirectory must be world-writable.
					 * However we don't want everybody to be able to know the
					 * sockets' filenames, so the directory is not readable.
					 */
					makeDirTree(path + "/backends", "u=rwx,g=wx,o=wx,+t");
				} else {
					/* All application processes are running as defaultUser/defaultGroup,
					 * so make defaultUser/defaultGroup the owner and group of the
					 * subdirecory.
					 *
					 * The directory is not readable as a security precaution:
					 * nobody should be able to know the sockets' filenames without
					 * having access to the application pool.
					 */
					makeDirTree(path + "/backends", "u=rwx,g=x,o=x", defaultUid, defaultGid);
				}
			} else {
				/* All application processes are running as the same user as the web server,
				 * so only allow access for this user.
				 */
				makeDirTree(path + "/backends", "u=rwx,g=,o=");
			}
			
			owner = true;
		}

Project: passenger
File: Utils.cpp

getHighestFileDescriptor() {
#if defined(F_MAXFD)
	int ret;
	
	do {
		ret = fcntl(0, F_MAXFD);
	} while (ret == -1 && errno == EINTR);
	if (ret == -1) {
		ret = getFileDescriptorLimit();
	}
	return ret;
	
#else
	int p[2], ret, flags;
	pid_t pid = -1;
	int result = -1;
	
	/* Since opendir() may not be async signal safe and thus may lock up
	 * or crash, we use it in a child process which we kill if we notice
	 * that things are going wrong.
	 */
	
	// Make a pipe.
	p[0] = p[1] = -1;
	do {
		ret = pipe(p);
	} while (ret == -1 && errno == EINTR);
	if (ret == -1) {
		goto done;
	}
	
	// Make the read side non-blocking.
	do {
		flags = fcntl(p[0], F_GETFL);
	} while (flags == -1 && errno == EINTR);
	if (flags == -1) {
		goto done;
	}
	do {
		fcntl(p[0], F_SETFL, flags | O_NONBLOCK);
	} while (ret == -1 && errno == EINTR);
	if (ret == -1) {
		goto done;
	}
	
	do {
		pid = asyncFork();
	} while (pid == -1 && errno == EINTR);
	
	if (pid == 0) {
		// Don't close p[0] here or it might affect the result.
		
		resetSignalHandlersAndMask();
		
		struct sigaction action;
		action.sa_handler = _exit;
		action.sa_flags   = SA_RESTART;
		sigemptyset(&action.sa_mask);
		sigaction(SIGSEGV, &action, NULL);
		sigaction(SIGPIPE, &action, NULL);
		sigaction(SIGBUS, &action, NULL);
		sigaction(SIGILL, &action, NULL);
		sigaction(SIGFPE, &action, NULL);
		sigaction(SIGABRT, &action, NULL);
		
		DIR *dir = NULL;
		#ifdef __APPLE__
			/* /dev/fd can always be trusted on OS X. */
			dir = opendir("/dev/fd");
		#else
			/* On FreeBSD and possibly other operating systems, /dev/fd only
			 * works if fdescfs is mounted. If it isn't mounted then /dev/fd
			 * still exists but always returns [0, 1, 2] and thus can't be
			 * trusted. If /dev and /dev/fd are on different filesystems
			 * then that probably means fdescfs is mounted.
			 */
			struct stat dirbuf1, dirbuf2;
			if (stat("/dev", &dirbuf1) == -1
			 || stat("/dev/fd", &dirbuf2) == -1) {
				_exit(1);
			}
			if (dirbuf1.st_dev != dirbuf2.st_dev) {
				dir = opendir("/dev/fd");
			}
		#endif
		if (dir == NULL) {
			dir = opendir("/proc/self/fd");
			if (dir == NULL) {
				_exit(1);
			}
		}
		
		struct dirent *ent;
		union {
			int highest;
			char data[sizeof(int)];
		} u;
		u.highest = -1;
		
		while ((ent = readdir(dir)) != NULL) {
			if (ent->d_name[0] != '.') {
				int number = atoi(ent->d_name);
				if (number > u.highest) {
					u.highest = number;
				}
			}
		}
		if (u.highest != -1) {
			ssize_t ret, written = 0;
			do {
				ret = write(p[1], u.data + written, sizeof(int) - written);
				if (ret == -1) {
					_exit(1);
				}
				written += ret;
			} while (written < (ssize_t) sizeof(int));
		}
		closedir(dir);
		_exit(0);
		
	} else if (pid == -1) {
		goto done;
		
	} else {
		close(p[1]); // Do not retry on EINTR: http://news.ycombinator.com/item?id=3363819
		p[1] = -1;
		
		union {
			int highest;
			char data[sizeof(int)];
		} u;
		ssize_t ret, bytesRead = 0;
		struct pollfd pfd;
		pfd.fd = p[0];
		pfd.events = POLLIN;
		
		do {
			do {
				// The child process must finish within 30 ms, otherwise
				// we might as well query sysconf.
				ret = poll(&pfd, 1, 30);
			} while (ret == -1 && errno == EINTR);
			if (ret <= 0) {
				goto done;
			}
			
			do {
				ret = read(p[0], u.data + bytesRead, sizeof(int) - bytesRead);
			} while (ret == -1 && ret == EINTR);
			if (ret == -1) {
				if (errno != EAGAIN) {
					goto done;
				}
			} else if (ret == 0) {
				goto done;
			} else {
				bytesRead += ret;
			}
		} while (bytesRead < (ssize_t) sizeof(int));
		
		result = u.highest;
		goto done;
	}

done:
	// Do not retry on EINTR: http://news.ycombinator.com/item?id=3363819
	if (p[0] != -1) {
		close(p[0]);
	}
	if (p[1] != -1) {
		close(p[1]);
	}
	if (pid != -1) {
		do {
			ret = kill(pid, SIGKILL);
		} while (ret == -1 && errno == EINTR);
		do {
			ret = waitpid(pid, NULL, 0);
		} while (ret == -1 && errno == EINTR);
	}
	
	if (result == -1) {
		result = getFileDescriptorLimit();
	}
	return result;
#endif
}

Project: passenger
File: ServerInstanceDir.h

	void destroy() {
		if (owner) {
			GenerationPtr newestGeneration;
			try {
				newestGeneration = getNewestGeneration();
			} catch (const FileSystemException &e) {
				if (e.code() == ENOENT) {
					return;
				} else {
					throw;
				}
			}
			if (newestGeneration == NULL) {
				removeDirTree(path);
			}
		}
	}

Project: passenger
File: Utils.cpp

absolutizePath(const StaticString &path, const StaticString &workingDir) {
	vector<string> components;
	if (!startsWith(path, "/")) {
		if (workingDir.empty()) {
			char buffer[PATH_MAX];
			if (getcwd(buffer, sizeof(buffer)) == NULL) {
				int e = errno;
				throw SystemException("Unable to query current working directory", e);
			}
			split(buffer + 1, '/', components);
		} else {
			string absoluteWorkingDir = absolutizePath(workingDir);
			split(StaticString(absoluteWorkingDir.data() + 1, absoluteWorkingDir.size() - 1),
				'/', components);
		}
	}

	const char *begin = path.data();
	const char *end = path.data() + path.size();

	// Skip leading slashes.
	while (begin < end && *begin == '/') {
		begin++;
	}

	while (begin < end) {
		const char *next = (const char *) memchr(begin, '/', end - begin);
		if (next == NULL) {
			next = end;
		}

		StaticString component(begin, next - begin);
		if (component == "..") {
			if (!components.empty()) {
				components.pop_back();
			}
		} else if (component != ".") {
			components.push_back(component);
		}

		// Skip slashes until beginning of next path component.
		begin = next + 1;
		while (begin < end && *begin == '/') {
			begin++;
		}
	}

	string result;
	vector<string>::const_iterator c_it, c_end = components.end();
	for (c_it = components.begin(); c_it != c_end; c_it++) {
		result.append("/");
		result.append(*c_it);
	}
	if (result.empty()) {
		result = "/";
	}
	return result;
}

Project: passenger
File: ServerInstanceDir.h

	GenerationPtr getGeneration(unsigned int number) const {
		// Must not used boost::make_shared() here because Watchdog.cpp
		// deletes the raw pointer in cleanupAgentsInBackground().
		return ptr(new Generation(path, number));
	}

Project: passenger
File: ServerInstanceDir.h

		unsigned int getNumber() const {
			return number;
		}

Project: passenger
File: Utils.cpp

createFile(const string &filename, const StaticString &contents, mode_t permissions, uid_t owner,
	gid_t group, bool overwrite)
{
	FileDescriptor fd;
	int ret, e, options;
	
	options = O_WRONLY | O_CREAT | O_TRUNC;
	if (!overwrite) {
		options |= O_EXCL;
	}
	do {
		fd = open(filename.c_str(), options, permissions);
	} while (fd == -1 && errno == EINTR);
	if (fd != -1) {
		FileGuard guard(filename);
		
		// The file permission may not be as expected because of the active
		// umask, so fchmod() it here to ensure correct permissions.
		do {
			ret = fchmod(fd, permissions);
		} while (ret == -1 && errno == EINTR);
		if (ret == -1) {
			e = errno;
			throw FileSystemException("Cannot set permissions on " + filename,
				e, filename);
		}
		
		if (owner != USER_NOT_GIVEN && group != GROUP_NOT_GIVEN) {
			if (owner == USER_NOT_GIVEN) {
				owner = (uid_t) -1; // Don't let fchown change file owner.
			}
			if (group == GROUP_NOT_GIVEN) {
				group = (gid_t) -1; // Don't let fchown change file group.
			}
			do {
				ret = fchown(fd, owner, group);
			} while (ret == -1 && errno == EINTR);
			if (ret == -1) {
				e = errno;
				throw FileSystemException("Cannot set ownership for " + filename,
					e, filename);
			}
		}
		
		try {
			writeExact(fd, contents);
			fd.close();
		} catch (const SystemException &e) {
			throw FileSystemException("Cannot write to file " + filename,
				e.code(), filename);
		}
		guard.commit();
	} else {
		e = errno;
		if (overwrite || e != EEXIST) {
			throw FileSystemException("Cannot create file " + filename,
				e, filename);
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

		void detach() {
			owner = false;
		}

Project: passenger
File: Utils.cpp

resolveSymlink(const StaticString &path) {
	char buf[PATH_MAX];
	ssize_t size;
	
	size = readlink(path.c_str(), buf, sizeof(buf) - 1);
	if (size == -1) {
		if (errno == EINVAL) {
			return path;
		} else {
			int e = errno;
			string message = "Cannot resolve possible symlink '";
			message.append(path.c_str(), path.size());
			message.append("'");
			throw FileSystemException(message, e, path);
		}
	} else {
		buf[size] = '\0';
		if (buf[0] == '\0') {
			string message = "The file '";
			message.append(path.c_str(), path.size());
			message.append("' is a symlink, and it refers to an empty filename. This is not allowed.");
			throw FileSystemException(message, ENOENT, path);
		} else if (buf[0] == '/') {
			// Symlink points to an absolute path.
			return buf;
		} else {
			return extractDirName(path) + "/" + buf;
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

	GenerationPtr newGeneration(bool userSwitching, const string &defaultUser,
	                            const string &defaultGroup, uid_t webServerWorkerUid,
	                            gid_t webServerWorkerGid)
	{
		GenerationPtr newestGeneration = getNewestGeneration();
		unsigned int newNumber;
		if (newestGeneration != NULL) {
			newNumber = newestGeneration->getNumber() + 1;
		} else {
			newNumber = 0;
		}
		
		GenerationPtr generation(new Generation(path, newNumber));
		generation->create(userSwitching, defaultUser, defaultGroup,
			webServerWorkerUid, webServerWorkerGid);
		return generation;
	}

Project: passenger
File: Utils.cpp

		~FileGuard() {
			if (!committed) {
				int ret;
				do {
					ret = unlink(filename.c_str());
				} while (ret == -1 && errno == EINTR);
			}
		}

Project: passenger
File: Utils.cpp

prestartWebApps(const ResourceLocator &locator, const string &ruby,
	const vector<string> &prestartURLs)
{
	/* Apache calls the initialization routines twice during startup, and
	 * as a result it starts two helper servers, where the first one exits
	 * after a short idle period. We want any prespawning requests to reach
	 * the second helper server, so we sleep for a short period before
	 * executing the prespawning scripts.
	 */
	syscalls::sleep(2);
	
	this_thread::disable_interruption di;
	this_thread::disable_syscall_interruption dsi;
	vector<string>::const_iterator it;
	string prespawnScript = locator.getHelperScriptsDir() + "/prespawn";
	
	it = prestartURLs.begin();
	while (it != prestartURLs.end() && !this_thread::interruption_requested()) {
		if (it->empty()) {
			it++;
			continue;
		}
		
		pid_t pid;
		
		pid = fork();
		if (pid == 0) {
			long max_fds, i;
			int e;
			
			// Close all unnecessary file descriptors.
			max_fds = sysconf(_SC_OPEN_MAX);
			for (i = 3; i < max_fds; i++) {
				syscalls::close(i);
			}
			
			execlp(ruby.c_str(),
				ruby.c_str(),
				prespawnScript.c_str(),
				it->c_str(),
				(char *) 0);
			e = errno;
			fprintf(stderr, "Cannot execute '%s %s': %s (%d)\n",
				prespawnScript.c_str(), it->c_str(),
				strerror(e), e);
			fflush(stderr);
			_exit(1);
		} else if (pid == -1) {
			perror("fork()");
		} else {
			try {
				this_thread::restore_interruption si(di);
				this_thread::restore_syscall_interruption ssi(dsi);
				syscalls::waitpid(pid, NULL, 0);
			} catch (const thread_interrupted &) {
				syscalls::kill(SIGKILL, pid);
				syscalls::waitpid(pid, NULL, 0);
				throw;
			}
		}
		
		this_thread::restore_interruption si(di);
		this_thread::restore_syscall_interruption ssi(dsi);
		syscalls::sleep(1);
		it++;
	}
}

Project: passenger
File: Utils.cpp

asyncFork() {
	#if defined(__linux__)
		#if defined(SYS_fork)
			return (pid_t) syscall(SYS_fork);
		#else
			return syscall(SYS_clone, SIGCHLD, 0, 0, 0, 0);
		#endif
	#elif defined(__APPLE__)
		return __fork();
	#else
		return fork();
	#endif
}

Project: passenger
File: Utils.cpp

breakpoint() {
	// No-op.
}

Project: passenger
File: Utils.cpp

getSignalName(int sig) {
	switch (sig) {
	case SIGHUP:
		return "SIGHUP";
	case SIGINT:
		return "SIGINT";
	case SIGQUIT:
		return "SIGQUIT";
	case SIGILL:
		return "SIGILL";
	case SIGTRAP:
		return "SIGTRAP";
	case SIGABRT:
		return "SIGABRT";
	case SIGFPE:
		return "SIGFPE";
	case SIGKILL:
		return "SIGKILL";
	case SIGBUS:
		return "SIGBUS";
	case SIGSEGV:
		return "SIGSEGV";
	case SIGPIPE:
		return "SIGPIPE";
	case SIGALRM:
		return "SIGARLM";
	case SIGTERM:
		return "SIGTERM";
	case SIGUSR1:
		return "SIGUSR1";
	case SIGUSR2:
		return "SIGUSR2";
	#ifdef SIGEMT
		case SIGEMT:
			return "SIGEMT";
	#endif
	#ifdef SIGINFO
		case SIGINFO:
			return "SIGINFO";
	#endif
	default:
		return toString(sig);
	}
}

Project: passenger
File: Utils.cpp

disableMallocDebugging() {
	unsetenv("MALLOC_FILL_SPACE");
	unsetenv("MALLOC_PROTECT_BEFORE");
	unsetenv("MallocGuardEdges");
	unsetenv("MallocScribble");
	unsetenv("MallocPreScribble");
	unsetenv("MallocCheckHeapStart");
	unsetenv("MallocCheckHeapEach");
	unsetenv("MallocCheckHeapAbort");
	unsetenv("MallocBadFreeAbort");
	unsetenv("MALLOC_CHECK_");

	const char *libs = getenv("DYLD_INSERT_LIBRARIES");
	if (libs != NULL && strstr(libs, "/usr/lib/libgmalloc.dylib")) {
		string newLibs = libs;
		string::size_type pos = newLibs.find("/usr/lib/libgmalloc.dylib");
		size_t len = strlen("/usr/lib/libgmalloc.dylib");

		// Erase all leading ':' too.
		while (pos > 0 && newLibs[pos - 1] == ':') {
			pos--;
			len++;
		}
		// Erase all trailing ':' too.
		while (pos + len < newLibs.size() && newLibs[pos + len] == ':') {
			len++;
		}

		newLibs.erase(pos, len);
		if (newLibs.empty()) {
			unsetenv("DYLD_INSERT_LIBRARIES");
		} else {
			setenv("DYLD_INSERT_LIBRARIES", newLibs.c_str(), 1);
		}
	}
}

Project: passenger
File: Utils.cpp

lookupGid(const string &groupName) {
	struct group *groupEntry;

	groupEntry = getgrnam(groupName.c_str());
	if (groupEntry == NULL) {
		if (looksLikePositiveNumber(groupName)) {
			return atoi(groupName);
		} else {
			return (gid_t) -1;
		}
	} else {
		return groupEntry->gr_gid;
	}
}

Project: passenger
File: Utils.cpp

canonicalizePath(const string &path) {
	#ifdef __GLIBC__
		// We're using a GNU extension here. See the 'BUGS'
		// section of the realpath(3) Linux manpage for
		// rationale.
		char *tmp = realpath(path.c_str(), NULL);
		if (tmp == NULL) {
			int e = errno;
			string message;
			
			message = "Cannot resolve the path '";
			message.append(path);
			message.append("'");
			throw FileSystemException(message, e, path);
		} else {
			string result(tmp);
			free(tmp);
			return result;
		}
	#else
		char tmp[PATH_MAX];
		if (realpath(path.c_str(), tmp) == NULL) {
			int e = errno;
			string message;
			
			message = "Cannot resolve the path '";
			message.append(path);
			message.append("'");
			throw FileSystemException(message, e, path);
		} else {
			return tmp;
		}
	#endif
}

Project: passenger
File: Utils.cpp

runAndPrintExceptions(const boost::function<void ()> &func) {
	runAndPrintExceptions(func, true);
}

Project: passenger
File: Utils.cpp

getHostName() {
	long hostNameMax = HOST_NAME_MAX;
	if (hostNameMax < 255) {
		// https://bugzilla.redhat.com/show_bug.cgi?id=130733
		hostNameMax = 255;
	}

	string buf(hostNameMax + 1, '\0');
	if (gethostname(&buf[0], hostNameMax + 1) == 0) {
		buf[hostNameMax] = '\0';
		return string(buf.c_str());
	} else {
		int e = errno;
		throw SystemException("Unable to query the system's host name", e);
	}
}

Project: passenger
File: Utils.cpp

runAndPrintExceptions(const boost::function<void ()> &func, bool toAbort) {
	try {
		func();
	} catch (const boost::thread_interrupted &) {
		throw;
	} catch (const tracable_exception &e) {
		P_ERROR("Exception: " << e.what() << "\n" << e.backtrace());
		if (toAbort) {
			abort();
		}
	}
}

Project: passenger
File: Utils.cpp

getGroupName(gid_t gid) {
	struct group *groupEntry;

	groupEntry = getgrgid(gid);
	if (groupEntry == NULL) {
		return toString(gid);
	} else {
		return groupEntry->gr_name;
	}
}

Project: passenger
File: Utils.cpp

extractDirNameStatic(const StaticString &path) {
	if (path.empty()) {
		return StaticString(".", 1);
	}

	const char *data = path.data();
	const char *end = path.data() + path.size();

	// Ignore trailing '/' characters.
	while (end > data && end[-1] == '/') {
		end--;
	}
	if (end == data) {
		// Apparently the entire path consists of slashes.
		return StaticString("/", 1);
	}

	// Find last '/'.
	end--;
	while (end > data && *end != '/') {
		end--;
	}
	if (end == data) {
		if (*data == '/') {
			// '/' found, but it's the first character in the path.
			return StaticString("/", 1);
		} else {
			// No '/' found in path.
			return StaticString(".", 1);
		}
	} else {
		// '/' found and it's not the first character in path.
		// 'end' points to that '/' character.
		// Skip to first non-'/' character.
		while (end >= data && *end == '/') {
			end--;
		}
		if (end < data) {
			// The entire path consists of '/' characters.
			return StaticString("/", 1);
		} else {
			return StaticString(data, end - data + 1);
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

	~ServerInstanceDir() {
		destroy();
	}

Project: passenger
File: Utils.cpp

resetSignalHandlersAndMask() {
	sigset_t signal_set;
	int ret;
	
	sigemptyset(&signal_set);
	do {
		ret = sigprocmask(SIG_SETMASK, &signal_set, NULL);
	} while (ret == -1 && errno == EINTR);
	
	struct sigaction action;
	action.sa_handler = SIG_DFL;
	action.sa_flags   = SA_RESTART;
	sigemptyset(&action.sa_mask);
	sigaction(SIGHUP,  &action, NULL);
	sigaction(SIGINT,  &action, NULL);
	sigaction(SIGQUIT, &action, NULL);
	sigaction(SIGILL,  &action, NULL);
	sigaction(SIGTRAP, &action, NULL);
	sigaction(SIGABRT, &action, NULL);
	#ifdef SIGEMT
		sigaction(SIGEMT,  &action, NULL);
	#endif
	sigaction(SIGFPE,  &action, NULL);
	sigaction(SIGBUS,  &action, NULL);
	sigaction(SIGSEGV, &action, NULL);
	sigaction(SIGSYS,  &action, NULL);
	sigaction(SIGPIPE, &action, NULL);
	sigaction(SIGALRM, &action, NULL);
	sigaction(SIGTERM, &action, NULL);
	sigaction(SIGURG,  &action, NULL);
	sigaction(SIGSTOP, &action, NULL);
	sigaction(SIGTSTP, &action, NULL);
	sigaction(SIGCONT, &action, NULL);
	sigaction(SIGCHLD, &action, NULL);
	#ifdef SIGINFO
		sigaction(SIGINFO, &action, NULL);
	#endif
	sigaction(SIGUSR1, &action, NULL);
	sigaction(SIGUSR2, &action, NULL);
}

Project: passenger
File: Utils.cpp

getFileType(const StaticString &filename, CachedFileStat *cstat, unsigned int throttleRate) {
	struct stat buf;
	int ret;
	
	if (cstat != NULL) {
		ret = cstat->stat(filename, &buf, throttleRate);
	} else {
		ret = stat(filename.c_str(), &buf);
	}
	if (ret == 0) {
		if (S_ISREG(buf.st_mode)) {
			return FT_REGULAR;
		} else if (S_ISDIR(buf.st_mode)) {
			return FT_DIRECTORY;
		} else {
			return FT_OTHER;
		}
	} else {
		if (errno == ENOENT) {
			return FT_NONEXISTANT;
		} else {
			int e = errno;
			string message("Cannot stat '");
			message.append(filename);
			message.append("'");
			throw FileSystemException(message, e, filename);
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

	GenerationPtr getNewestGeneration() const {
		DIR *dir = opendir(path.c_str());
		struct dirent *entry;
		int result = -1;
		
		if (dir == NULL) {
			int e = errno;
			throw FileSystemException("Cannot open directory " + path,
				e, path);
		}
		while ((entry = readdir(dir)) != NULL) {
			if (isDirectory(path, entry)
			 && strncmp(entry->d_name, "generation-", sizeof("generation-") - 1) == 0) {
				const char *numberString = entry->d_name + sizeof("generation-") - 1;
				int number = atoi(numberString);
				if (number >= 0 && number > result) {
					result = number;
				}
			}
		}
		closedir(dir);
		
		if (result == -1) {
			return GenerationPtr();
		} else {
			return getGeneration(result);
		}
	}

Project: passenger
File: ServerInstanceDir.h

		Generation(const string &serverInstanceDir, unsigned int number) {
			path = serverInstanceDir + "/generation-" + toString(number);
			this->number = number;
			owner = false;
		}

Project: passenger
File: Utils.cpp

escapeForXml(const string &input) {
	string result(input);
	string::size_type input_pos = 0;
	string::size_type input_end_pos = input.size();
	string::size_type result_pos = 0;
	
	while (input_pos < input_end_pos) {
		const unsigned char ch = input[input_pos];
		
		if ((ch >= 'A' && ch <= 'z')
		 || (ch >= '0' && ch <= '9')
		 || ch == '/' || ch == ' ' || ch == '_' || ch == '.'
		 || ch == ':' || ch == '+' || ch == '-') {
			// This is an ASCII character. Ignore it and
			// go to next character.
			result_pos++;
		} else {
			// Not an ASCII character; escape it.
			char escapedCharacter[sizeof("&#255;") + 1];
			int size;
			
			size = snprintf(escapedCharacter,
				sizeof(escapedCharacter) - 1,
				"&#%d;",
				(int) ch);
			if (size < 0) {
				throw std::bad_alloc();
			}
			escapedCharacter[sizeof(escapedCharacter) - 1] = '\0';
			
			result.replace(result_pos, 1, escapedCharacter, size);
			result_pos += size;
		}
		input_pos++;
	}
	
	return result;
}

Project: passenger
File: Utils.cpp

extractDirName(const StaticString &path) {
	char *path_copy = strdup(path.c_str());
	char *result = dirname(path_copy);
	string result_string(result);
	free(path_copy);
	return result_string;
}

Project: passenger
File: Utils.cpp

		FileGuard(const string &filename) {
			this->filename = filename;
			committed = false;
		}

Project: passenger
File: ServerInstanceDir.h

		void destroy() {
			if (owner) {
				removeDirTree(path);
			}
		}

Project: passenger
File: Utils.cpp

		void commit() {
			committed = true;
		}

Project: passenger
File: Utils.cpp

getProcessUsername() {
	struct passwd pwd, *result;
	char strings[1024];
	int ret;
	
	result = (struct passwd *) NULL;
	do {
		ret = getpwuid_r(getuid(), &pwd, strings, sizeof(strings), &result);
	} while (ret == -1 && errno == EINTR);
	if (ret == -1) {
		result = (struct passwd *) NULL;
	}
	
	if (result == (struct passwd *) NULL || result->pw_name == NULL || result->pw_name[0] == '\0') {
		snprintf(strings, sizeof(strings), "UID %lld", (long long) getuid());
		strings[sizeof(strings) - 1] = '\0';
		return strings;
	} else {
		return result->pw_name;
	}
}

Project: passenger
File: Utils.cpp

makeDirTree(const string &path, const StaticString &mode, uid_t owner, gid_t group) {
	struct stat buf;
	vector<string> paths;
	vector<string>::reverse_iterator rit;
	string current = path;
	mode_t modeBits;
	int ret;
	
	if (stat(path.c_str(), &buf) == 0) {
		return;
	}
	
	modeBits = parseModeString(mode);
	
	/* Create a list of parent paths that don't exist. For example, given
	 * path == "/a/b/c/d/e" and that only /a exists, the list will become
	 * as follows:
	 *
	 * /a/b/c/d
	 * /a/b/c
	 * /a/b
	 */
	while (current != "/" && current != "." && getFileType(current) == FT_NONEXISTANT) {
		paths.push_back(current);
		current = extractDirName(current);
	}
	
	/* Now traverse the list in reverse order and create directories that don't exist. */
	for (rit = paths.rbegin(); rit != paths.rend(); rit++) {
		current = *rit;
		
		do {
			ret = mkdir(current.c_str(), modeBits);
		} while (ret == -1 && errno == EINTR);
		if (ret == -1) {
			if (errno == EEXIST) {
				// Ignore error and don't chmod/chown.
				continue;
			} else {
				int e = errno;
				throw FileSystemException("Cannot create directory '" + current + "'",
					e, current);
			}
		}
		
		/* Chmod in order to override the umask. */
		do {
			ret = chmod(current.c_str(), modeBits);
		} while (ret == -1 && errno == EINTR);
		
		if (owner != USER_NOT_GIVEN && group != GROUP_NOT_GIVEN) {
			if (owner == USER_NOT_GIVEN) {
				owner = (uid_t) -1; // Don't let chown change file owner.
			}
			if (group == GROUP_NOT_GIVEN) {
				group = (gid_t) -1; // Don't let chown change file group.
			}
			do {
				ret = chown(current.c_str(), owner, group);
			} while (ret == -1 && errno == EINTR);
			if (ret == -1) {
				char message[1024];
				int e = errno;
				
				snprintf(message, sizeof(message) - 1,
					"Cannot change the directory '%s' its UID to %lld and GID to %lld",
					current.c_str(), (long long) owner, (long long) group);
				message[sizeof(message) - 1] = '\0';
				throw FileSystemException(message, e, path);
			}
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

	ServerInstanceDir(const string &path, bool owner = true) {
		initialize(path, owner);
	}

Project: passenger
File: Utils.cpp

fileExists(const StaticString &filename, CachedFileStat *cstat, unsigned int throttleRate) {
	return getFileType(filename, cstat, throttleRate) == FT_REGULAR;
}

Project: passenger
File: Utils.cpp

removeDirTree(const string &path) {
	this_thread::disable_interruption di;
	this_thread::disable_syscall_interruption dsi;
	const char *c_path = path.c_str();
	pid_t pid;

	pid = syscalls::fork();
	if (pid == 0) {
		resetSignalHandlersAndMask();
		disableMallocDebugging();
		int devnull = open("/dev/null", O_RDONLY);
		if (devnull != -1) {
			dup2(devnull, 2);
		}
		closeAllFileDescriptors(2);
		execlp("chmod", "chmod", "-R", "u+rwx", c_path, (char * const) 0);
		perror("Cannot execute chmod");
		_exit(1);

	} else if (pid == -1) {
		int e = errno;
		throw SystemException("Cannot fork a new process", e);

	} else {
		this_thread::restore_interruption ri(di);
		this_thread::restore_syscall_interruption rsi(dsi);
		syscalls::waitpid(pid, NULL, 0);
	}

	pid = syscalls::fork();
	if (pid == 0) {
		resetSignalHandlersAndMask();
		disableMallocDebugging();
		closeAllFileDescriptors(2);
		execlp("rm", "rm", "-rf", c_path, (char * const) 0);
		perror("Cannot execute rm");
		_exit(1);

	} else if (pid == -1) {
		int e = errno;
		throw SystemException("Cannot fork a new process", e);

	} else {
		this_thread::restore_interruption ri(di);
		this_thread::restore_syscall_interruption rsi(dsi);
		int status;
		if (syscalls::waitpid(pid, &status, 0) == -1 || status != 0) {
			throw RuntimeException("Cannot remove directory '" + path + "'");
		}
	}
}

Project: passenger
File: ServerInstanceDir.h

		~Generation() {
			destroy();
		}

Project: passenger
File: Utils.cpp

getFileDescriptorLimit() {
	long long sysconfResult = sysconf(_SC_OPEN_MAX);
	
	struct rlimit rl;
	long long rlimitResult;
	if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
		rlimitResult = 0;
	} else {
		rlimitResult = (long long) rl.rlim_max;
	}
	
	long result;
	if (sysconfResult > rlimitResult) {
		result = sysconfResult;
	} else {
		result = rlimitResult;
	}
	if (result < 0) {
		// Both calls returned errors.
		result = 9999;
	} else if (result < 2) {
		// The calls reported broken values.
		result = 2;
	}
	return result;
}

Project: passenger
File: Utils.cpp

getSystemTempDir() {
	const char *temp_dir = getenv("PASSENGER_TEMP_DIR");
	if (temp_dir == NULL || *temp_dir == '\0') {
		temp_dir = getenv("PASSENGER_TMPDIR");
		if (temp_dir == NULL || *temp_dir == '\0') {
			temp_dir = "/tmp";
		}
	}
	return temp_dir;
}