passenger 4.0.14 → 4.0.16

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (121) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +69 -0
  3. data/bin/passenger-install-apache2-module +7 -1
  4. data/bin/passenger-install-nginx-module +18 -1
  5. data/build/apache2.rb +25 -1
  6. data/build/basics.rb +7 -4
  7. data/build/debian.rb +72 -25
  8. data/build/nginx.rb +24 -0
  9. data/build/packaging.rb +45 -3
  10. data/debian.template/{control → control.template} +17 -8
  11. data/debian.template/ruby-passenger.install.template +1 -0
  12. data/debian.template/rules.template +9 -3
  13. data/dev/run_travis.sh +14 -0
  14. data/doc/Users guide Apache.idmap.txt +64 -48
  15. data/doc/Users guide Apache.txt +127 -93
  16. data/doc/Users guide Nginx.idmap.txt +46 -28
  17. data/doc/Users guide Nginx.txt +100 -12
  18. data/doc/images/{conservative_spawning.png → direct_spawning.png} +0 -0
  19. data/doc/images/{conservative_spawning.svg → direct_spawning.svg} +0 -0
  20. data/doc/users_guide_snippets/installation.txt +107 -66
  21. data/doc/users_guide_snippets/passenger_spawn_method.txt +1 -1
  22. data/doc/users_guide_snippets/rvm_helper_tool.txt +0 -12
  23. data/doc/users_guide_snippets/tips.txt +70 -3
  24. data/doc/users_guide_snippets/under_the_hood/page_caching_support.txt +2 -0
  25. data/doc/users_guide_snippets/under_the_hood/relationship_with_ruby.txt +114 -0
  26. data/ext/apache2/Configuration.cpp +53 -101
  27. data/ext/apache2/Configuration.hpp +19 -41
  28. data/ext/apache2/ConfigurationCommands.cpp +95 -0
  29. data/ext/apache2/ConfigurationCommands.cpp.erb +91 -0
  30. data/ext/apache2/ConfigurationFields.hpp +59 -0
  31. data/ext/apache2/ConfigurationFields.hpp.erb +89 -0
  32. data/ext/apache2/ConfigurationSetters.cpp +223 -0
  33. data/ext/apache2/ConfigurationSetters.cpp.erb +126 -0
  34. data/ext/apache2/CreateDirConfig.cpp +50 -0
  35. data/ext/apache2/CreateDirConfig.cpp.erb +71 -0
  36. data/ext/apache2/Hooks.cpp +6 -0
  37. data/ext/apache2/MergeDirConfig.cpp +103 -0
  38. data/ext/apache2/MergeDirConfig.cpp.erb +81 -0
  39. data/ext/common/ApplicationPool2/AppTypes.cpp +2 -0
  40. data/ext/common/ApplicationPool2/AppTypes.h +2 -0
  41. data/ext/common/ApplicationPool2/Common.h +1 -1
  42. data/ext/common/ApplicationPool2/Group.h +56 -7
  43. data/ext/common/ApplicationPool2/Implementation.cpp +133 -31
  44. data/ext/common/ApplicationPool2/Options.h +23 -2
  45. data/ext/common/ApplicationPool2/Pool.h +8 -1
  46. data/ext/common/ApplicationPool2/Process.h +9 -0
  47. data/ext/common/ApplicationPool2/Session.h +1 -0
  48. data/ext/common/ApplicationPool2/Spawner.h +21 -19
  49. data/ext/common/ApplicationPool2/SuperGroup.h +1 -1
  50. data/ext/common/Constants.h +21 -17
  51. data/ext/common/Constants.h.erb +1 -1
  52. data/ext/common/Exceptions.h +19 -0
  53. data/ext/common/ServerInstanceDir.h +8 -4
  54. data/ext/common/Utils.cpp +33 -1
  55. data/ext/common/Utils.h +14 -0
  56. data/ext/common/Utils/StrIntUtils.cpp +16 -0
  57. data/ext/common/Utils/StrIntUtils.h +5 -0
  58. data/ext/common/agents/HelperAgent/Main.cpp +5 -5
  59. data/ext/common/agents/HelperAgent/RequestHandler.h +94 -45
  60. data/ext/common/agents/LoggingAgent/Main.cpp +10 -26
  61. data/ext/common/agents/Watchdog/Main.cpp +4 -15
  62. data/ext/nginx/CacheLocationConfig.c +501 -0
  63. data/ext/nginx/CacheLocationConfig.c.erb +140 -0
  64. data/ext/nginx/Configuration.c +29 -453
  65. data/ext/nginx/Configuration.h +3 -21
  66. data/ext/nginx/ConfigurationCommands.c +501 -0
  67. data/ext/nginx/ConfigurationCommands.c.erb +136 -0
  68. data/ext/nginx/ConfigurationFields.h +89 -0
  69. data/ext/nginx/ConfigurationFields.h.erb +85 -0
  70. data/ext/nginx/ContentHandler.c +3 -166
  71. data/ext/nginx/CreateLocationConfig.c +146 -0
  72. data/ext/nginx/CreateLocationConfig.c.erb +70 -0
  73. data/ext/nginx/MergeLocationConfig.c +166 -0
  74. data/ext/nginx/MergeLocationConfig.c.erb +72 -0
  75. data/ext/nginx/config +4 -0
  76. data/ext/oxt/detail/tracable_exception_disabled.hpp +21 -1
  77. data/ext/oxt/detail/tracable_exception_enabled.hpp +4 -1
  78. data/ext/oxt/implementation.cpp +7 -1
  79. data/ext/oxt/macros.hpp +9 -7
  80. data/helper-scripts/backtrace-sanitizer.rb +23 -0
  81. data/helper-scripts/classic-rails-loader.rb +23 -0
  82. data/helper-scripts/classic-rails-preloader.rb +23 -0
  83. data/helper-scripts/download_binaries/extconf.rb +10 -5
  84. data/helper-scripts/meteor-loader.rb +127 -0
  85. data/helper-scripts/node-loader.js +1 -1
  86. data/helper-scripts/rack-preloader.rb +23 -0
  87. data/helper-scripts/system-memory-stats.py +22 -0
  88. data/helper-scripts/touch-dir.sh +48 -0
  89. data/lib/phusion_passenger.rb +1 -1
  90. data/lib/phusion_passenger/apache2/config_options.rb +104 -0
  91. data/lib/phusion_passenger/constants.rb +8 -0
  92. data/lib/phusion_passenger/native_support.rb +9 -1
  93. data/lib/phusion_passenger/nginx/config_options.rb +328 -0
  94. data/lib/phusion_passenger/packaging.rb +2 -2
  95. data/lib/phusion_passenger/platform_info/apache.rb +8 -0
  96. data/lib/phusion_passenger/platform_info/compiler.rb +2 -2
  97. data/lib/phusion_passenger/rails3_extensions/init.rb +19 -4
  98. data/lib/phusion_passenger/ruby_core_enhancements.rb +4 -1
  99. data/lib/phusion_passenger/standalone/app_finder.rb +3 -2
  100. data/lib/phusion_passenger/standalone/command.rb +1 -0
  101. data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -6
  102. data/lib/phusion_passenger/standalone/runtime_locator.rb +9 -2
  103. data/lib/phusion_passenger/standalone/start_command.rb +45 -9
  104. data/lib/phusion_passenger/utils.rb +4 -1
  105. data/resources/templates/{nginx → installer_common}/run_installer_as_root.txt.erb +3 -1
  106. data/resources/templates/nginx/nginx_module_sources_not_available.txt.erb +6 -0
  107. data/resources/templates/standalone/config.erb +8 -8
  108. data/test/cxx/ApplicationPool2/PoolTest.cpp +120 -3
  109. data/test/cxx/RequestHandlerTest.cpp +66 -3
  110. data/test/integration_tests/native_packaging_spec.rb +41 -0
  111. data/test/integration_tests/nginx_tests.rb +1 -0
  112. data/test/integration_tests/standalone_tests.rb +4 -0
  113. data/test/ruby/shared/rails/analytics_logging_extensions_sharedspec.rb +22 -0
  114. data/test/stub/rails3.0/config/initializers/passenger.rb +2 -1
  115. data/test/stub/rails3.1/config/initializers/passenger.rb +2 -1
  116. data/test/stub/rails3.2/config/initializers/passenger.rb +2 -1
  117. data/test/stub/rails4.0/config/initializers/passenger.rb +5 -1
  118. data/test/stub/wsgi/passenger_wsgi.py +5 -0
  119. metadata +32 -7
  120. metadata.gz.asc +7 -7
  121. data/resources/templates/apache2/run_installer_as_root.txt.erb +0 -8
@@ -297,6 +297,20 @@ public:
297
297
 
298
298
  /** The number of seconds that preloader processes may stay alive idling. */
299
299
  long maxPreloaderIdleTime;
300
+
301
+ /**
302
+ * The maximum number of processes inside a group that may be performing
303
+ * out-of-band work at the same time.
304
+ */
305
+ unsigned int maxOutOfBandWorkInstances;
306
+
307
+ /**
308
+ * The maximum number of requests that may live in the Group.getWaitlist queue.
309
+ * A value of 0 means unlimited.
310
+ */
311
+ unsigned int maxRequestQueueSize;
312
+
313
+ /*-----------------*/
300
314
 
301
315
 
302
316
  /*********** Per-request options that should be set manually and that only matter to Pool ***********/
@@ -370,8 +384,8 @@ public:
370
384
  baseURI = "/";
371
385
  spawnMethod = "smart";
372
386
  defaultUser = "nobody";
373
- ruby = "ruby";
374
- python = "python";
387
+ ruby = DEFAULT_RUBY;
388
+ python = DEFAULT_PYTHON;
375
389
  rights = DEFAULT_BACKEND_ACCOUNT_RIGHTS;
376
390
  debugger = false;
377
391
  loadShellEnvvars = true;
@@ -380,6 +394,8 @@ public:
380
394
 
381
395
  minProcesses = 1;
382
396
  maxPreloaderIdleTime = -1;
397
+ maxOutOfBandWorkInstances = 1;
398
+ maxRequestQueueSize = 100;
383
399
 
384
400
  statThrottleRate = 0;
385
401
  maxRequests = 0;
@@ -528,6 +544,7 @@ public:
528
544
  if (fields & PER_GROUP_POOL_OPTIONS) {
529
545
  appendKeyValue3(vec, "min_processes", minProcesses);
530
546
  appendKeyValue2(vec, "max_preloader_idle_time", maxPreloaderIdleTime);
547
+ appendKeyValue3(vec, "max_out_of_band_work_instances", maxOutOfBandWorkInstances);
531
548
  }
532
549
 
533
550
  /*********************************/
@@ -567,6 +584,10 @@ public:
567
584
  return ruby + "\t" + resourceLocator.getHelperScriptsDir() + "/rack-loader.rb";
568
585
  } else if (appType == "wsgi") {
569
586
  return python + "\t" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
587
+ } else if (appType == "node") {
588
+ return "node\t" + resourceLocator.getHelperScriptsDir() + "/node-loader.js";
589
+ } else if (appType == "meteor") {
590
+ return ruby + "\t" + resourceLocator.getHelperScriptsDir() + "/meteor-loader.rb";
570
591
  } else {
571
592
  return startCommand;
572
593
  }
@@ -100,6 +100,7 @@ public:
100
100
  bool spawning;
101
101
  bool superGroup;
102
102
  bool oobw;
103
+ bool testOverflowRequestQueue;
103
104
 
104
105
  // The following fields may only be accessed by Pool.
105
106
  boost::mutex syncher;
@@ -112,6 +113,7 @@ public:
112
113
  spawning = true;
113
114
  superGroup = false;
114
115
  oobw = false;
116
+ testOverflowRequestQueue = false;
115
117
  spawnLoopIteration = 0;
116
118
  }
117
119
  };
@@ -571,7 +573,8 @@ public:
571
573
  process->lastUsed + maxIdleTime;
572
574
  if (process->sessions == 0
573
575
  && state.now >= processGcTime
574
- && (unsigned long) group->enabledCount > group->options.minProcesses) {
576
+ && (unsigned long) group->getProcessCount() > group->options.minProcesses)
577
+ {
575
578
  ProcessList::iterator prev = p_it;
576
579
  prev--;
577
580
  P_DEBUG("Garbage collect idle process: " << process->inspect() <<
@@ -1165,6 +1168,10 @@ public:
1165
1168
  return result;
1166
1169
  }
1167
1170
 
1171
+ /**
1172
+ * Returns the total number of processes in the pool, including all disabling and
1173
+ * disabled processes, but excluding processes that are shutting down.
1174
+ */
1168
1175
  unsigned int getProcessCount(bool lock = true) const {
1169
1176
  DynamicScopedLock l(syncher, lock);
1170
1177
  unsigned int result = 0;
@@ -465,6 +465,15 @@ public:
465
465
  return false;
466
466
  }
467
467
  }
468
+
469
+ /** Kill the OS process with the given signal. */
470
+ int kill(int signo) {
471
+ if (osProcessExists()) {
472
+ return syscalls::kill(pid, signo);
473
+ } else {
474
+ return 0;
475
+ }
476
+ }
468
477
 
469
478
  int utilization() const {
470
479
  /* Different processes within a Group may have different
@@ -110,6 +110,7 @@ public:
110
110
  const string &getGupid() const;
111
111
  const GroupPtr getGroup() const;
112
112
  void requestOOBW();
113
+ int kill(int signo);
113
114
 
114
115
  bool isClosed() const {
115
116
  return closed;
@@ -781,17 +781,10 @@ protected:
781
781
  getProcessUsername() + "; it looks like your system's " +
782
782
  "user database is broken, please fix it.");
783
783
  }
784
- struct group *groupInfo = getgrgid(userInfo->pw_gid);
785
- if (groupInfo == NULL) {
786
- throw RuntimeException(string("Cannot get group database entry for ") +
787
- "the default group belonging to username '" +
788
- getProcessUsername() + "'; it looks like your system's " +
789
- "user database is broken, please fix it.");
790
- }
791
784
 
792
785
  info.switchUser = false;
793
786
  info.username = userInfo->pw_name;
794
- info.groupname = groupInfo->gr_name;
787
+ info.groupname = getGroupName(userInfo->pw_gid);
795
788
  info.home = userInfo->pw_dir;
796
789
  info.shell = userInfo->pw_shell;
797
790
  info.uid = geteuid();
@@ -804,7 +797,7 @@ protected:
804
797
  string defaultGroup;
805
798
  string startupFile = absolutizePath(options.getStartupFile(), info.appRoot);
806
799
  struct passwd *userInfo = NULL;
807
- struct group *groupInfo = NULL;
800
+ gid_t groupId = (gid_t) -1;
808
801
 
809
802
  if (options.defaultGroup.empty()) {
810
803
  struct passwd *info = getpwnam(options.defaultUser.c_str());
@@ -848,22 +841,31 @@ protected:
848
841
  throw SystemException("Cannot lstat(\"" +
849
842
  startupFile + "\")", e);
850
843
  }
851
- groupInfo = getgrgid(buf.st_gid);
844
+ if (getgrgid(buf.st_gid) != NULL) {
845
+ groupId = buf.st_gid;
846
+ } else {
847
+ groupId = (gid_t) -1;
848
+ }
852
849
  } else {
853
- groupInfo = getgrnam(options.group.c_str());
850
+ struct group *groupInfo = getgrnam(options.group.c_str());
851
+ if (groupInfo != NULL) {
852
+ groupId = groupInfo->gr_gid;
853
+ } else {
854
+ groupId = (gid_t) -1;
855
+ }
854
856
  }
855
857
  } else if (userInfo != NULL) {
856
- groupInfo = getgrgid(userInfo->pw_gid);
858
+ groupId = userInfo->pw_gid;
857
859
  }
858
- if (groupInfo == NULL || groupInfo->gr_gid == 0) {
859
- groupInfo = getgrnam(defaultGroup.c_str());
860
+ if (groupId == 0 || groupId == (gid_t) -1) {
861
+ groupId = lookupGid(defaultGroup);
860
862
  }
861
-
863
+
862
864
  UPDATE_TRACE_POINT();
863
865
  if (userInfo == NULL) {
864
866
  throw RuntimeException("Cannot determine a user to lower privilege to");
865
867
  }
866
- if (groupInfo == NULL) {
868
+ if (groupId == (gid_t) -1) {
867
869
  throw RuntimeException("Cannot determine a group to lower privilege to");
868
870
  }
869
871
 
@@ -877,16 +879,16 @@ protected:
877
879
  #endif
878
880
  info.switchUser = true;
879
881
  info.username = userInfo->pw_name;
880
- info.groupname = groupInfo->gr_name;
882
+ info.groupname = getGroupName(groupId);
881
883
  info.home = userInfo->pw_dir;
882
884
  info.shell = userInfo->pw_shell;
883
885
  info.uid = userInfo->pw_uid;
884
- info.gid = groupInfo->gr_gid;
886
+ info.gid = groupId;
885
887
  #if !defined(HAVE_GETGROUPLIST) && (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__))
886
888
  #define HAVE_GETGROUPLIST
887
889
  #endif
888
890
  #ifdef HAVE_GETGROUPLIST
889
- int ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
891
+ int ret = getgrouplist(userInfo->pw_name, groupId,
890
892
  groups, &info.ngroups);
891
893
  if (ret == -1) {
892
894
  int e = errno;
@@ -656,7 +656,7 @@ public:
656
656
  vector<GroupPtr>::const_iterator g_it, g_end = groups.end();
657
657
  for (g_it = groups.begin(); g_it != g_end; g_it++) {
658
658
  const GroupPtr &group = *g_it;
659
- result += group->enabledCount + group->disablingCount + group->disabledCount;
659
+ result += group->getProcessCount();
660
660
  }
661
661
  return result;
662
662
  }
@@ -36,51 +36,55 @@
36
36
  #define DEFAULT_BACKEND_ACCOUNT_RIGHTS Account::DETACH
37
37
 
38
38
 
39
- #define DEFAULT_LOG_LEVEL 0
40
-
41
- #define DEFAULT_RUBY "ruby"
39
+ #define DEFAULT_ANALYTICS_LOG_GROUP ""
42
40
 
43
- #define DEFAULT_PYTHON "python"
41
+ #define DEFAULT_ANALYTICS_LOG_PERMISSIONS "u=rwx,g=rx,o=rx"
44
42
 
45
- #define DEFAULT_MAX_POOL_SIZE 6
43
+ #define DEFAULT_ANALYTICS_LOG_USER "nobody"
46
44
 
47
- #define DEFAULT_POOL_IDLE_TIME 300
45
+ #define DEFAULT_LOG_LEVEL 0
48
46
 
49
47
  #define DEFAULT_MAX_INSTANCES_PER_APP 0
50
48
 
51
- #define DEFAULT_WEB_APP_USER "nobody"
49
+ #define DEFAULT_MAX_POOL_SIZE 6
52
50
 
53
- #define DEFAULT_ANALYTICS_LOG_USER "nobody"
51
+ #define DEFAULT_POOL_IDLE_TIME 300
54
52
 
55
- #define DEFAULT_ANALYTICS_LOG_GROUP ""
53
+ #define DEFAULT_PYTHON "python"
56
54
 
57
- #define DEFAULT_ANALYTICS_LOG_PERMISSIONS "u=rwx,g=rx,o=rx"
55
+ #define DEFAULT_RUBY "ruby"
56
+
57
+ #define DEFAULT_START_TIMEOUT 90000
58
58
 
59
59
  #define DEFAULT_UNION_STATION_GATEWAY_ADDRESS "gateway.unionstationapp.com"
60
60
 
61
61
  #define DEFAULT_UNION_STATION_GATEWAY_PORT 443
62
62
 
63
- #define MESSAGE_SERVER_MAX_USERNAME_SIZE 100
63
+ #define DEFAULT_WEB_APP_USER "nobody"
64
+
65
+ #define FEEDBACK_FD 3
64
66
 
65
67
  #define MESSAGE_SERVER_MAX_PASSWORD_SIZE 100
66
68
 
69
+ #define MESSAGE_SERVER_MAX_USERNAME_SIZE 100
70
+
71
+ #define PASSENGER_VERSION "4.0.16"
72
+
67
73
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
68
74
 
69
75
  #define PROCESS_SHUTDOWN_TIMEOUT 60
70
76
 
71
77
  #define PROCESS_SHUTDOWN_TIMEOUT_DISPLAY "1 minute"
72
78
 
73
- #define PASSENGER_VERSION "4.0.14"
79
+ #define SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MAJOR_VERSION 2
80
+
81
+ #define SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MINOR_VERSION 0
74
82
 
75
83
  #define SERVER_INSTANCE_DIR_STRUCTURE_MAJOR_VERSION 1
76
84
 
77
85
  #define SERVER_INSTANCE_DIR_STRUCTURE_MINOR_VERSION 0
78
86
 
79
- #define SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MAJOR_VERSION 2
80
-
81
- #define SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MINOR_VERSION 0
82
-
83
- #define FEEDBACK_FD 3
87
+ #define STANDALONE_NGINX_CONFIGURE_OPTIONS "--with-cc-opt='-Wno-error' --without-http_fastcgi_module --without-http_scgi_module --without-http_uwsgi_module --with-http_gzip_static_module --with-http_stub_status_module"
84
88
 
85
89
 
86
90
  #endif /* _PASSENGER_CONSTANTS_H */
@@ -35,7 +35,7 @@
35
35
 
36
36
  #define DEFAULT_BACKEND_ACCOUNT_RIGHTS Account::DETACH
37
37
 
38
- <% for constant_name in PhusionPassenger::SharedConstants.constants %>
38
+ <% for constant_name in PhusionPassenger::SharedConstants.constants.sort %>
39
39
  #define <%= constant_name %> <%=PhusionPassenger::SharedConstants.const_get(constant_name).inspect %>
40
40
  <% end %>
41
41
 
@@ -316,6 +316,10 @@ public:
316
316
  : msg(message)
317
317
  { }
318
318
 
319
+ GetAbortedException(const oxt::tracable_exception::no_backtrace &tag)
320
+ : oxt::tracable_exception(tag)
321
+ { }
322
+
319
323
  virtual ~GetAbortedException() throw() {}
320
324
 
321
325
  virtual const char *what() const throw() {
@@ -323,6 +327,21 @@ public:
323
327
  }
324
328
  };
325
329
 
330
+ /**
331
+ * Indicates that a Pool::get() or Pool::asyncGet() request was denied because
332
+ * the getWaitlist queue was full.
333
+ */
334
+ class RequestQueueFullException: public GetAbortedException {
335
+ public:
336
+ RequestQueueFullException()
337
+ : GetAbortedException(oxt::tracable_exception::no_backtrace())
338
+ { }
339
+
340
+ virtual const char *what() const throw() {
341
+ return "Request queue is full";
342
+ }
343
+ };
344
+
326
345
  /**
327
346
  * Indicates that a specified argument is incorrect or violates a requirement.
328
347
  *
@@ -82,7 +82,6 @@ public:
82
82
  TRACE_POINT();
83
83
  bool runningAsRoot = geteuid() == 0;
84
84
  struct passwd *defaultUserEntry;
85
- struct group *defaultGroupEntry;
86
85
  uid_t defaultUid;
87
86
  gid_t defaultGid;
88
87
 
@@ -92,12 +91,11 @@ public:
92
91
  "' does not exist.");
93
92
  }
94
93
  defaultUid = defaultUserEntry->pw_uid;
95
- defaultGroupEntry = getgrnam(defaultGroup.c_str());
96
- if (defaultGroupEntry == NULL) {
94
+ defaultGid = lookupGid(defaultGroup);
95
+ if (defaultGid == (gid_t) -1) {
97
96
  throw NonExistentGroupException("Default group '" + defaultGroup +
98
97
  "' does not exist.");
99
98
  }
100
- defaultGid = defaultGroupEntry->gr_gid;
101
99
 
102
100
  /* We set a very tight permission here: no read or write access for
103
101
  * anybody except the owner. The individual files and subdirectories
@@ -251,6 +249,12 @@ private:
251
249
  throw FileSystemException("Cannot create server instance directory '" +
252
250
  path + "'", e, path);
253
251
  }
252
+ // Explicitly chmod the directory in case the umask is interfering.
253
+ if (chmod(path.c_str(), parseModeString("u=rwx,g=rx,o=rx")) == -1) {
254
+ int e = errno;
255
+ throw FileSystemException("Cannot set permissions on server instance directory '" +
256
+ path + "'", e, path);
257
+ }
254
258
  // verifyDirectoryPermissions() checks for the owner/group so we must make
255
259
  // sure the server instance directory has that owner/group, even when the
256
260
  // parent directory has setgid on.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010, 2011, 2012 Phusion
3
+ * Copyright (c) 2010-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -400,6 +400,38 @@ getProcessUsername() {
400
400
  }
401
401
  }
402
402
 
403
+ string
404
+ getGroupName(gid_t gid) {
405
+ struct group *groupEntry;
406
+
407
+ groupEntry = getgrgid(gid);
408
+ if (groupEntry == NULL) {
409
+ return toString(gid);
410
+ } else {
411
+ return groupEntry->gr_name;
412
+ }
413
+ }
414
+
415
+ gid_t
416
+ lookupGid(const StaticString &groupName) {
417
+ struct group *groupEntry;
418
+ char name[groupName.size() + 1];
419
+
420
+ memcpy(name, groupName.data(), groupName.size());
421
+ name[groupName.size()] = '\0';
422
+
423
+ groupEntry = getgrnam(name);
424
+ if (groupEntry == NULL) {
425
+ if (looksLikePositiveNumber(groupName)) {
426
+ return atoi(name);
427
+ } else {
428
+ return (gid_t) -1;
429
+ }
430
+ } else {
431
+ return groupEntry->gr_gid;
432
+ }
433
+ }
434
+
403
435
  mode_t
404
436
  parseModeString(const StaticString &mode) {
405
437
  mode_t modeBits = 0;
@@ -210,6 +210,20 @@ string escapeForXml(const string &input);
210
210
  */
211
211
  string getProcessUsername();
212
212
 
213
+ /**
214
+ * Returns either the group name for the given GID, or (if the group name
215
+ * couldn't be looked up) a string representation of the given GID.
216
+ */
217
+ string getGroupName(gid_t gid);
218
+
219
+ /**
220
+ * Given a `groupName` which is either the name of a group, or a string
221
+ * containing the GID of a group, looks up the GID as a gid_t.
222
+ *
223
+ * Returns `(gid_t) -1` if the lookup fails.
224
+ */
225
+ gid_t lookupGid(const StaticString &groupName);
226
+
213
227
  /**
214
228
  * Converts a mode string into a mode_t value.
215
229
  *
@@ -374,6 +374,22 @@ integerToHexatri(long long value) {
374
374
  return string(buf);
375
375
  }
376
376
 
377
+ bool
378
+ looksLikePositiveNumber(const StaticString &str) {
379
+ if (str.empty()) {
380
+ return false;
381
+ } else {
382
+ bool result = true;
383
+ const char *data = str.data();
384
+ const char *end = str.data() + str.size();
385
+ while (result && data != end) {
386
+ result = result && (*data >= '0' && *data <= '9');
387
+ data++;
388
+ }
389
+ return result;
390
+ }
391
+ }
392
+
377
393
  int
378
394
  atoi(const string &s) {
379
395
  return ::atoi(s.c_str());