static void spawn_win32(void) { char module_name[WATCHMAN_NAME_MAX]; GetModuleFileName(NULL, module_name, sizeof(module_name)); char *argv[MAX_DAEMON_ARGS] = { module_name, "--foreground", NULL }; posix_spawn_file_actions_t actions; posix_spawnattr_t attr; pid_t pid; int i; for (i = 0; daemon_argv[i]; i++) { append_argv(argv, daemon_argv[i]); } posix_spawnattr_init(&attr); posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP); posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0); posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600); posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDERR_FILENO); posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ); posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); }
static void spawn_via_gimli(void) { char *argv[MAX_DAEMON_ARGS] = { GIMLI_MONITOR_PATH, #ifdef WATCHMAN_STATE_DIR "--trace-dir=" WATCHMAN_STATE_DIR "/traces", #endif "--pidfile", pid_file, "watchman", "--foreground", NULL }; posix_spawn_file_actions_t actions; posix_spawnattr_t attr; pid_t pid; int i; for (i = 0; daemon_argv[i]; i++) { append_argv(argv, daemon_argv[i]); } close_random_fds(); posix_spawnattr_init(&attr); posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600); posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDERR_FILENO); posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ); posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); }
// Spawn watchman via a site-specific spawn helper program. // We'll pass along any daemon-appropriate arguments that // we noticed during argument parsing. static void spawn_site_specific(const char *spawner) { char *argv[MAX_DAEMON_ARGS] = { (char*)spawner, NULL }; posix_spawn_file_actions_t actions; posix_spawnattr_t attr; pid_t pid; int i; int res, err; for (i = 0; daemon_argv[i]; i++) { append_argv(argv, daemon_argv[i]); } close_random_fds(); posix_spawnattr_init(&attr); posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600); posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDERR_FILENO); res = posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ); err = errno; posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); if (res) { w_log(W_LOG_FATAL, "Failed to spawn watchman via `%s': %s\n", spawner, strerror(err)); } if (waitpid(pid, &res, 0) == -1) { w_log(W_LOG_FATAL, "Failed waiting for %s: %s\n", spawner, strerror(errno)); } if (WIFEXITED(res) && WEXITSTATUS(res) == 0) { return; } if (WIFEXITED(res)) { w_log(W_LOG_FATAL, "%s: exited with status %d\n", spawner, WEXITSTATUS(res)); } else if (WIFSIGNALED(res)) { w_log(W_LOG_FATAL, "%s: signaled with %d\n", spawner, WTERMSIG(res)); } w_log(W_LOG_ERR, "%s: failed to start, exit status %d\n", spawner, res); }
static void spawn_via_launchd(void) { char watchman_path[WATCHMAN_NAME_MAX]; uint32_t size = sizeof(watchman_path); char plist_path[WATCHMAN_NAME_MAX]; FILE *fp; struct passwd *pw; uid_t uid; char *argv[MAX_DAEMON_ARGS] = { "/bin/launchctl", "load", "-F", NULL }; posix_spawnattr_t attr; pid_t pid; int res; close_random_fds(); if (_NSGetExecutablePath(watchman_path, &size) == -1) { w_log(W_LOG_ERR, "_NSGetExecutablePath: path too long; size %u\n", size); abort(); } uid = getuid(); pw = getpwuid(uid); if (!pw) { w_log(W_LOG_ERR, "getpwuid(%d) failed: %s. I don't know who you are\n", uid, strerror(errno)); abort(); } snprintf(plist_path, sizeof(plist_path), "%s/Library/LaunchAgents", pw->pw_dir); // Best effort attempt to ensure that the agents dir exists. We'll detect // and report the failure in the fopen call below. mkdir(plist_path, 0755); snprintf(plist_path, sizeof(plist_path), "%s/Library/LaunchAgents/com.github.facebook.watchman.plist", pw->pw_dir); if (access(plist_path, R_OK) == 0) { // Unload any that may already exist, as it is likely wrong char *unload_argv[MAX_DAEMON_ARGS] = { "/bin/launchctl", "unload", NULL }; append_argv(unload_argv, plist_path); errno = posix_spawnattr_init(&attr); if (errno != 0) { w_log(W_LOG_FATAL, "posix_spawnattr_init: %s\n", strerror(errno)); } res = posix_spawnp(&pid, unload_argv[0], NULL, &attr, unload_argv, environ); if (res == 0) { waitpid(pid, &res, 0); } posix_spawnattr_destroy(&attr); // Forcibly remove the plist. In some cases it may have some attributes // set that prevent launchd from loading it. This can happen where // the system was re-imaged or restored from a backup unlink(plist_path); } fp = fopen(plist_path, "w"); if (!fp) { w_log(W_LOG_ERR, "Failed to open %s for write: %s\n", plist_path, strerror(errno)); abort(); } compute_file_name(&pid_file, compute_user_name(), "pid", "pidfile"); fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "<plist version=\"1.0\">\n" "<dict>\n" " <key>Label</key>\n" " <string>com.github.facebook.watchman</string>\n" " <key>Disabled</key>\n" " <false/>\n" " <key>ProgramArguments</key>\n" " <array>\n" " <string>%s</string>\n" " <string>--foreground</string>\n" " <string>--logfile=%s</string>\n" " <string>--log-level=%d</string>\n" " <string>--sockname=%s</string>\n" " <string>--statefile=%s</string>\n" " <string>--pidfile=%s</string>\n" " </array>\n" " <key>Sockets</key>\n" " <dict>\n" " <key>sock</key>\n" // coupled with get_listener_socket_from_launchd " <dict>\n" " <key>SockPathName</key>\n" " <string>%s</string>\n" " <key>SockPathMode</key>\n" " <integer>%d</integer>\n" " </dict>\n" " </dict>\n" " <key>KeepAlive</key>\n" " <dict>\n" " <key>Crashed</key>\n" " <true/>\n" " </dict>\n" " <key>RunAtLoad</key>\n" " <true/>\n" " <key>EnvironmentVariables</key>\n" " <dict>\n" " <key>PATH</key>\n" " <string><![CDATA[%s]]></string>\n" " </dict>\n" " <key>ProcessType</key>\n" " <string>Interactive</string>\n" " <key>Nice</key>\n" " <integer>-5</integer>\n" "</dict>\n" "</plist>\n", watchman_path, log_name, log_level, sock_name, watchman_state_file, pid_file, sock_name, 0600, getenv("PATH")); fclose(fp); // Don't rely on umask, ensure we have the correct perms chmod(plist_path, 0644); append_argv(argv, plist_path); errno = posix_spawnattr_init(&attr); if (errno != 0) { w_log(W_LOG_FATAL, "posix_spawnattr_init: %s\n", strerror(errno)); } res = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ); if (res) { w_log(W_LOG_FATAL, "Failed to spawn watchman via launchd: %s\n", strerror(errno)); } posix_spawnattr_destroy(&attr); if (waitpid(pid, &res, 0) == -1) { w_log(W_LOG_FATAL, "Failed waiting for launchctl load: %s\n", strerror(errno)); } if (WIFEXITED(res) && WEXITSTATUS(res) == 0) { return; } // Most likely cause is "headless" operation with no GUI context if (WIFEXITED(res)) { w_log(W_LOG_ERR, "launchctl: exited with status %d\n", WEXITSTATUS(res)); } else if (WIFSIGNALED(res)) { w_log(W_LOG_ERR, "launchctl: signaled with %d\n", WTERMSIG(res)); } w_log(W_LOG_ERR, "Falling back to daemonize\n"); daemonize(); }
static void spawn_via_launchd(void) { char watchman_path[WATCHMAN_NAME_MAX]; uint32_t size = sizeof(watchman_path); char plist_path[WATCHMAN_NAME_MAX]; FILE *fp; struct passwd *pw; uid_t uid; char *argv[MAX_DAEMON_ARGS] = { "/bin/launchctl", "load", "-F", NULL }; posix_spawnattr_t attr; pid_t pid; int res; if (_NSGetExecutablePath(watchman_path, &size) == -1) { w_log(W_LOG_ERR, "_NSGetExecutablePath: path too long; size %u\n", size); abort(); } uid = getuid(); pw = getpwuid(uid); if (!pw) { w_log(W_LOG_ERR, "getpwuid(%d) failed: %s. I don't know who you are\n", uid, strerror(errno)); abort(); } snprintf(plist_path, sizeof(plist_path), "%s/Library/LaunchAgents", pw->pw_dir); // Best effort attempt to ensure that the agents dir exists. We'll detect // and report the failure in the fopen call below. mkdir(plist_path, 0755); snprintf(plist_path, sizeof(plist_path), "%s/Library/LaunchAgents/com.github.facebook.watchman.plist", pw->pw_dir); fp = fopen(plist_path, "w"); if (!fp) { w_log(W_LOG_ERR, "Failed to open %s for write: %s\n", plist_path, strerror(errno)); abort(); } fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "<plist version=\"1.0\">\n" "<dict>\n" " <key>Label</key>\n" " <string>com.github.facebook.watchman</string>\n" " <key>Disabled</key>\n" " <false/>\n" " <key>ProgramArguments</key>\n" " <array>\n" " <string>%s</string>\n" " <string>--foreground</string>\n" " <string>--logfile=%s</string>\n" " <string>--sockname=%s</string>\n" " <string>--statefile=%s</string>\n" " </array>\n" " <key>Sockets</key>\n" " <dict>\n" " <key>sock</key>\n" // coupled with get_listener_socket_from_launchd " <dict>\n" " <key>SockPathName</key>\n" " <string>%s</string>\n" " </dict>\n" " </dict>\n" " <key>KeepAlive</key>\n" " <dict>\n" " <key>Crashed</key>\n" " <true/>\n" " </dict>\n" " <key>RunAtLoad</key>\n" " <false/>\n" "</dict>\n" "</plist>\n", watchman_path, log_name, sock_name, watchman_state_file, sock_name); fclose(fp); append_argv(argv, plist_path); errno = posix_spawnattr_init(&attr); if (errno != 0) { w_log(W_LOG_FATAL, "posix_spawnattr_init: %s\n", strerror(errno)); } res = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ); if (res) { w_log(W_LOG_FATAL, "Failed to spawn watchman via launchd: %s\n", strerror(errno)); } posix_spawnattr_destroy(&attr); if (waitpid(pid, &res, 0) == -1) { w_log(W_LOG_FATAL, "Failed waiting for launchctl load: %s\n", strerror(errno)); } if (WIFEXITED(res) && WEXITSTATUS(res) == 0) { return; } // Most likely cause is "headless" operation with no GUI context if (WIFEXITED(res)) { w_log(W_LOG_ERR, "launchctl: exited with status %d\n", WEXITSTATUS(res)); } else if (WIFSIGNALED(res)) { w_log(W_LOG_ERR, "launchctl: signalled with %d\n", WTERMSIG(res)); } w_log(W_LOG_ERR, "Falling back to daemonize\n"); daemonize(); }