| OLD | NEW |
| 1 // Copyright (c) 2009-2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009-2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "login_manager/session_manager_service.h" | 5 #include "login_manager/session_manager_service.h" |
| 6 | 6 |
| 7 #include <glib.h> | 7 #include <glib.h> |
| 8 #include <grp.h> | 8 #include <grp.h> |
| 9 #include <sys/errno.h> | 9 #include <sys/errno.h> |
| 10 #include <sys/types.h> | 10 #include <sys/types.h> |
| 11 #include <sys/wait.h> | 11 #include <sys/wait.h> |
| 12 #include <signal.h> | 12 #include <signal.h> |
| 13 #include <stdio.h> | 13 #include <stdio.h> |
| 14 #include <unistd.h> | 14 #include <unistd.h> |
| 15 | 15 |
| 16 #include <base/basictypes.h> | 16 #include <base/basictypes.h> |
| 17 #include <base/command_line.h> | 17 #include <base/command_line.h> |
| 18 #include <base/logging.h> | 18 #include <base/logging.h> |
| 19 #include <base/string_util.h> |
| 19 #include <chromeos/dbus/dbus.h> | 20 #include <chromeos/dbus/dbus.h> |
| 20 | 21 |
| 21 #include "login_manager/child_job.h" | 22 #include "login_manager/child_job.h" |
| 22 #include "login_manager/interface.h" | 23 #include "login_manager/interface.h" |
| 23 | 24 |
| 24 // Forcibly namespace the dbus-bindings generated server bindings instead of | 25 // Forcibly namespace the dbus-bindings generated server bindings instead of |
| 25 // modifying the files afterward. | 26 // modifying the files afterward. |
| 26 namespace login_manager { // NOLINT | 27 namespace login_manager { // NOLINT |
| 27 namespace gobject { // NOLINT | 28 namespace gobject { // NOLINT |
| 28 #include "login_manager/bindings/server.h" | 29 #include "login_manager/bindings/server.h" |
| 29 } // namespace gobject | 30 } // namespace gobject |
| 30 } // namespace login_manager | 31 } // namespace login_manager |
| 31 | 32 |
| 32 namespace login_manager { | 33 namespace login_manager { |
| 33 | 34 |
| 35 using std::string; |
| 36 |
| 37 //static |
| 38 const uint32 SessionManagerService::kMaxEmailSize = 200; |
| 39 //static |
| 40 const char SessionManagerService::kEmailSeparator = '@'; |
| 41 //static |
| 42 const char SessionManagerService::kLegalCharacters[] = |
| 43 "abcdefghijklmnopqrstuvwxyz" |
| 44 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 45 ".@1234567890"; |
| 46 |
| 34 SessionManagerService::SessionManagerService(ChildJob* child) | 47 SessionManagerService::SessionManagerService(ChildJob* child) |
| 35 : child_job_(child), | 48 : child_job_(child), |
| 36 exit_on_child_done_(false), | 49 exit_on_child_done_(false), |
| 37 main_loop_(g_main_loop_new(NULL, FALSE)) { | 50 child_pgid_(0), |
| 51 main_loop_(g_main_loop_new(NULL, FALSE)), |
| 52 system_(new SystemUtils) { |
| 38 CHECK(child); | 53 CHECK(child); |
| 39 SetupHandlers(); | 54 SetupHandlers(); |
| 40 } | 55 } |
| 41 | |
| 42 SessionManagerService::SessionManagerService(ChildJob* child, | |
| 43 bool exit_on_child_done) | |
| 44 : child_job_(child), | |
| 45 exit_on_child_done_(exit_on_child_done), | |
| 46 main_loop_(g_main_loop_new(NULL, FALSE)) { | |
| 47 CHECK(child); | |
| 48 SetupHandlers(); | |
| 49 } | |
| 50 | 56 |
| 51 SessionManagerService::~SessionManagerService() { | 57 SessionManagerService::~SessionManagerService() { |
| 52 g_main_loop_unref(main_loop_); | 58 g_main_loop_unref(main_loop_); |
| 53 | 59 |
| 54 struct sigaction action; | 60 struct sigaction action; |
| 55 memset(&action, 0, sizeof(action)); | 61 memset(&action, 0, sizeof(action)); |
| 56 action.sa_handler = SIG_DFL; | 62 action.sa_handler = SIG_DFL; |
| 57 CHECK(sigaction(SIGUSR1, &action, NULL) == 0); | 63 CHECK(sigaction(SIGUSR1, &action, NULL) == 0); |
| 58 } | 64 } |
| 59 | 65 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 83 return false; | 89 return false; |
| 84 } | 90 } |
| 85 return true; | 91 return true; |
| 86 } | 92 } |
| 87 | 93 |
| 88 bool SessionManagerService::Run() { | 94 bool SessionManagerService::Run() { |
| 89 if (!main_loop_) { | 95 if (!main_loop_) { |
| 90 LOG(ERROR) << "You must have a main loop to call Run."; | 96 LOG(ERROR) << "You must have a main loop to call Run."; |
| 91 return false; | 97 return false; |
| 92 } | 98 } |
| 93 int pid = RunChild(); | |
| 94 if (pid == -1) { | |
| 95 // We couldn't fork...maybe we should wait and try again later? | |
| 96 PLOG(ERROR) << "Failed to fork!"; | |
| 97 | 99 |
| 100 if (should_run_child()) { |
| 101 int pid = RunChild(); |
| 102 if (pid == -1) { |
| 103 // We couldn't fork...maybe we should wait and try again later? |
| 104 PLOG(ERROR) << "Failed to fork!"; |
| 105 return false; |
| 106 } |
| 107 child_pgid_ = -pid; |
| 98 } else { | 108 } else { |
| 99 // In the parent. | 109 AllowGracefulExit(); |
| 100 g_main_loop_run(main_loop_); | |
| 101 } | 110 } |
| 111 |
| 112 // In the parent. |
| 113 g_main_loop_run(main_loop_); |
| 114 |
| 115 if (child_pgid_ != 0) // otherwise, we never created a child. |
| 116 CleanupChildren(3); |
| 117 |
| 102 return true; | 118 return true; |
| 103 } | 119 } |
| 104 | 120 |
| 105 int SessionManagerService::RunChild() { | 121 int SessionManagerService::RunChild() { |
| 106 int pid = fork(); | 122 int pid = fork(); |
| 107 if (pid == 0) { | 123 if (pid == 0) { |
| 108 // In the child. | 124 // In the child. |
| 109 child_job_->Run(); | 125 child_job_->Run(); |
| 110 exit(1); // Run() is not supposed to return. | 126 exit(1); // Run() is not supposed to return. |
| 111 } | 127 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 125 NULL); | 141 NULL); |
| 126 } | 142 } |
| 127 } | 143 } |
| 128 | 144 |
| 129 /////////////////////////////////////////////////////////////////////////////// | 145 /////////////////////////////////////////////////////////////////////////////// |
| 130 // SessionManagerService commands | 146 // SessionManagerService commands |
| 131 | 147 |
| 132 gboolean SessionManagerService::EmitLoginPromptReady(gboolean *OUT_emitted, | 148 gboolean SessionManagerService::EmitLoginPromptReady(gboolean *OUT_emitted, |
| 133 GError **error) { | 149 GError **error) { |
| 134 DLOG(INFO) << "emitting login-prompt-ready "; | 150 DLOG(INFO) << "emitting login-prompt-ready "; |
| 135 system("/sbin/initctl emit login-prompt-ready &"); | 151 *OUT_emitted = system("/sbin/initctl emit login-prompt-ready &") == 0; |
| 136 *OUT_emitted = TRUE; | 152 return *OUT_emitted; |
| 137 return TRUE; | |
| 138 } | 153 } |
| 139 | 154 |
| 140 gboolean SessionManagerService::StartSession(gchar *email_address, | 155 gboolean SessionManagerService::StartSession(gchar *email_address, |
| 141 gchar *unique_identifier, | 156 gchar *unique_identifier, |
| 142 gboolean *OUT_done, | 157 gboolean *OUT_done, |
| 143 GError **error) { | 158 GError **error) { |
| 144 DLOG(INFO) << "emitting start-user-session"; | 159 // basic validity checking; avoid buffer overflows here, and |
| 145 system("/sbin/initctl emit start-user-session &"); | 160 // canonicalize the email address a little. |
| 146 child_job_->Toggle(); | 161 char email[kMaxEmailSize + 1]; |
| 147 *OUT_done = TRUE; | 162 snprintf(email, sizeof(email), "%s", email_address); |
| 148 return TRUE; | 163 email[kMaxEmailSize] = '\0'; // Just to be sure. |
| 164 string email_string(email); |
| 165 if (!ValidateEmail(email_string)) { |
| 166 *OUT_done = FALSE; |
| 167 return FALSE; |
| 168 } |
| 169 string email_lower = StringToLowerASCII(email_string); |
| 170 DLOG(INFO) << "emitting start-user-session for " << email_lower; |
| 171 string command = |
| 172 StringPrintf("/sbin/initctl emit start-user-session CHROMEOS_USER=%s &", |
| 173 email_lower.c_str()); |
| 174 *OUT_done = system(command.c_str()) == 0; |
| 175 if (*OUT_done) |
| 176 child_job_->Toggle(); |
| 177 return *OUT_done; |
| 149 } | 178 } |
| 150 | 179 |
| 151 gboolean SessionManagerService::StopSession(gchar *unique_identifier, | 180 gboolean SessionManagerService::StopSession(gchar *unique_identifier, |
| 152 gboolean *OUT_done, | 181 gboolean *OUT_done, |
| 153 GError **error) { | 182 GError **error) { |
| 154 DLOG(INFO) << "emitting stop-user-session"; | 183 DLOG(INFO) << "emitting stop-user-session"; |
| 155 system("/sbin/initctl emit stop-user-session &"); | 184 *OUT_done = system("/sbin/initctl emit stop-user-session &") == 0; |
| 156 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, | 185 if (*OUT_done) { |
| 157 ServiceShutdown, | 186 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, |
| 158 this, | 187 ServiceShutdown, |
| 159 NULL); | 188 this, |
| 160 child_job_->Toggle(); | 189 NULL); |
| 161 *OUT_done = TRUE; | 190 child_job_->Toggle(); |
| 162 return TRUE; | 191 } |
| 192 return *OUT_done; |
| 163 } | 193 } |
| 164 | 194 |
| 165 | 195 |
| 166 /////////////////////////////////////////////////////////////////////////////// | 196 /////////////////////////////////////////////////////////////////////////////// |
| 167 // glib event handlers | 197 // glib event handlers |
| 168 | 198 |
| 169 void SessionManagerService::HandleChildExit(GPid pid, | 199 void SessionManagerService::HandleChildExit(GPid pid, |
| 170 gint status, | 200 gint status, |
| 171 gpointer data) { | 201 gpointer data) { |
| 172 // If I could wait for descendants here, I would. Instead, I kill them. | 202 // If I could wait for descendants here, I would. Instead, I kill them. |
| 173 kill(-pid, SIGKILL); | 203 kill(-pid, SIGKILL); |
| 174 | 204 |
| 175 DLOG(INFO) << "exited waitpid.\n" | 205 DLOG(INFO) << "exited waitpid.\n" |
| 176 << " WIFSIGNALED is " << WIFSIGNALED(status) << "\n" | 206 << " WIFSIGNALED is " << WIFSIGNALED(status) << "\n" |
| 177 << " WTERMSIG is " << WTERMSIG(status) << "\n" | 207 << " WTERMSIG is " << WTERMSIG(status) << "\n" |
| 178 << " WIFEXITED is " << WIFEXITED(status) << "\n" | 208 << " WIFEXITED is " << WIFEXITED(status) << "\n" |
| 179 << " WEXITSTATUS is " << WEXITSTATUS(status); | 209 << " WEXITSTATUS is " << WEXITSTATUS(status); |
| 180 if (WIFEXITED(status)) { | 210 if (WIFEXITED(status)) { |
| 181 CHECK(WEXITSTATUS(status) != SetUidExecJob::kCantSetuid); | 211 CHECK(WEXITSTATUS(status) != SetUidExecJob::kCantSetuid); |
| 182 CHECK(WEXITSTATUS(status) != SetUidExecJob::kCantExec); | 212 CHECK(WEXITSTATUS(status) != SetUidExecJob::kCantExec); |
| 183 } | 213 } |
| 184 | 214 |
| 185 // If the child _ever_ exits, we want to start it up again. | 215 // If the child _ever_ exits, we want to start it up again. |
| 186 SessionManagerService* manager = static_cast<SessionManagerService*>(data); | 216 SessionManagerService* manager = static_cast<SessionManagerService*>(data); |
| 187 if (manager->should_run_child()) { | 217 if (manager->should_run_child()) { |
| 188 manager->RunChild(); | 218 // TODO(cmasone): deal with fork failing in RunChild() |
| 219 manager->set_child_pgid(-manager->RunChild()); |
| 189 } else { | 220 } else { |
| 190 LOG(INFO) << "Should NOT run"; | 221 LOG(INFO) << "Should NOT run"; |
| 191 manager->AllowGracefulExit(); | 222 manager->AllowGracefulExit(); |
| 192 } | 223 } |
| 193 } | 224 } |
| 194 | 225 |
| 195 gboolean SessionManagerService::ServiceShutdown(gpointer data) { | 226 gboolean SessionManagerService::ServiceShutdown(gpointer data) { |
| 196 SessionManagerService* manager = static_cast<SessionManagerService*>(data); | 227 SessionManagerService* manager = static_cast<SessionManagerService*>(data); |
| 197 manager->Shutdown(); | 228 manager->Shutdown(); |
| 198 return FALSE; // So that the event source that called this gets removed. | 229 return FALSE; // So that the event source that called this gets removed. |
| 199 } | 230 } |
| 200 | 231 |
| 201 | 232 |
| 202 | 233 |
| 203 /////////////////////////////////////////////////////////////////////////////// | 234 /////////////////////////////////////////////////////////////////////////////// |
| 204 // Utility Methods | 235 // Utility Methods |
| 205 | 236 |
| 237 // This can probably be more efficient, if it needs to be. |
| 238 // static |
| 239 bool SessionManagerService::ValidateEmail(const string& email_address) { |
| 240 if (email_address.find_first_not_of(kLegalCharacters) != string::npos) |
| 241 return false; |
| 242 |
| 243 size_t at = email_address.find(kEmailSeparator); |
| 244 // it has NO @. |
| 245 if (at == string::npos) |
| 246 return false; |
| 247 |
| 248 // it has more than one @. |
| 249 if (email_address.find(kEmailSeparator, at+1) != string::npos) |
| 250 return false; |
| 251 |
| 252 return true; |
| 253 } |
| 254 |
| 206 void SessionManagerService::SetupHandlers() { | 255 void SessionManagerService::SetupHandlers() { |
| 207 // I have to ignore SIGUSR1, because Xorg sends it to this process when it's | 256 // I have to ignore SIGUSR1, because Xorg sends it to this process when it's |
| 208 // got no clients and is ready for new ones. If we don't ignore it, we die. | 257 // got no clients and is ready for new ones. If we don't ignore it, we die. |
| 209 struct sigaction chld_action; | 258 struct sigaction chld_action; |
| 210 memset(&chld_action, 0, sizeof(chld_action)); | 259 memset(&chld_action, 0, sizeof(chld_action)); |
| 211 chld_action.sa_handler = SIG_IGN; | 260 chld_action.sa_handler = SIG_IGN; |
| 212 CHECK(sigaction(SIGUSR1, &chld_action, NULL) == 0); | 261 CHECK(sigaction(SIGUSR1, &chld_action, NULL) == 0); |
| 213 } | 262 } |
| 214 | 263 |
| 264 void SessionManagerService::CleanupChildren(int max_tries) { |
| 265 int try_count = 0; |
| 266 while(!system_->child_is_gone(child_pgid_)) { |
| 267 system_->kill(child_pgid_, (try_count++ >= max_tries ? SIGKILL : SIGTERM)); |
| 268 // TODO(cmasone): add conversion constants/methods in common/ somewhere. |
| 269 usleep(500 * 1000 /* milliseconds */); |
| 270 } |
| 271 } |
| 272 |
| 215 } // namespace login_manager | 273 } // namespace login_manager |
| OLD | NEW |