static int pipe_attach_remote_writer (pipe_t *self, const char *remote, bool unicast) { assert (self); if (self->reader == REMOTE_NODE) { // We're witnessing two nodes chatting, so we can drop the pipe // and forget all about it pipe_destroy (&self); return 0; } else if (self->writer == NULL) { // This is how we indicate a remote writer self->writer = REMOTE_NODE; self->remote = strdup (remote); zsys_info ("%s: attach remote writer", self->name); if (self->reader && !unicast) { // Tell remote node we're acting as reader, if we got a // broadcast message. If we got a unicast message, the peer // already knows about us, so don't re-echo the message zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "HAVE READER"); zmsg_addstr (msg, self->name); zyre_whisper (self->server->zyre, self->remote, &msg); zsys_info ("%s: tell peer we are now reader", self->name); } return 0; } zsys_info ("%s: pipe already has writer: ignored", self->name); return -1; }
static void pipe_drop_local_writer (pipe_t **self_p) { assert (self_p); if (*self_p) { pipe_t *self = *self_p; // TODO: what if self->writer is REMOTE_NODE? self->writer = NULL; if (self->reader) { if (self->reader == REMOTE_NODE) { // Tell remote node we're dropping off zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "DROP WRITER"); zmsg_addstr (msg, self->name); zyre_whisper (self->server->zyre, self->remote, &msg); zsys_info ("%s: tell peer we stopped being writer", self->name); } else { engine_send_event (self->reader, writer_dropped_event); // Don't destroy pipe yet - reader is still using it *self_p = NULL; } } pipe_destroy (self_p); } }
static int pipe_check_availability(void) { grpc_wakeup_fd fd; if (pipe_init(&fd) == GRPC_ERROR_NONE) { pipe_destroy(&fd); return 1; } else { return 0; } }
void event_exit_platform(void) { semaphore_destroy(&_usb_poll_resume); semaphore_destroy(&_usb_poll_suspend); array_destroy(&_usb_poll_pollfds, NULL); pipe_destroy(&_usb_poll_ready_pipe); usbi_close(_usb_poll_suspend_pipe[0]); usbi_close(_usb_poll_suspend_pipe[1]); event_remove_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); pipe_destroy(&_stop_pipe); free(_socket_error_set); free(_socket_write_set); free(_socket_read_set); }
void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2->fd >= 0) { eloop_unregister_read_sock(l2->fd); pipe_destroy(l2->fd); } os_free(l2); }
void iokit_exit(void) { log_debug("Shutting down IOKit subsystem"); if (_running) { _running = false; CFRunLoopStop(_run_loop); CFRelease(_run_loop); thread_join(&_poll_thread); } thread_destroy(&_poll_thread); event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); pipe_destroy(&_notification_pipe); }
static void pipe_drop_remote_writer (pipe_t **self_p, const char *remote) { assert (self_p); if (*self_p) { pipe_t *self = *self_p; if (self->writer == REMOTE_NODE && streq (self->remote, remote)) { self->writer = NULL; if (self->reader) { assert (self->reader != REMOTE_NODE); engine_send_event (self->reader, writer_dropped_event); // Don't destroy pipe yet - reader is still using it *self_p = NULL; } } pipe_destroy (self_p); } }
static void process_destroy(Object *object) { Process *process = (Process *)object; int rc; bool stuck = false; // remove the state change pipe from the event loop to avoid sending // callbacks in case the child process is still alive and has to be killed event_remove_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); // FIXME: this code here has the same race condition as process_kill if (process_is_alive(process)) { log_warn("Destroying process object (id: %u, executable: %s) while child process (pid: %u) is still alive", process->base.id, process->executable->buffer, process->pid); rc = kill(process->pid, SIGKILL); if (rc < 0) { if (errno != ESRCH) { stuck = true; } log_error("Could not send SIGKILL signal to child process (executable: %s, pid: %u): %s (%d)", process->executable->buffer, process->pid, get_errno_name(errno), errno); } } if (!stuck) { thread_join(&process->wait_thread); } thread_destroy(&process->wait_thread); pipe_destroy(&process->state_change_pipe); file_release(process->stderr); file_release(process->stdout); file_release(process->stdin); string_unlock_and_release(process->working_directory); list_unlock_and_release(process->environment); list_unlock_and_release(process->arguments); string_unlock_and_release(process->executable); free(process); }
DWORD CE_Cleanup(PCONFIGURATION_ENTRY pce) { DWORD dwErr = NO_ERROR; int i; if (pce->hRtmNotificationHandle) { dwErr = RtmDeregisterFromChangeNotification( pce->hRtmHandle, pce->hRtmNotificationHandle); if (dwErr != NO_ERROR) TRACE1(CONFIGURATION, "Error %u deregistering for change from RTM", dwErr); } pce->hRtmNotificationHandle = NULL; if (pce->hRtmHandle) { dwErr = RtmDeregisterEntity(pce->hRtmHandle); if (dwErr != NO_ERROR) TRACE1(CONFIGURATION, "Error %u deregistering from RTM", dwErr); } pce->hRtmHandle = NULL; for (i = 0; i < PIPE_INSTANCES; i++) { if (pce->pipes[i]) { pipe_destroy(pce->pipes[i]); pce->pipes[i] = NULL; } } if (pce->hMprConfig != NULL) { MprConfigServerDisconnect(pce->hMprConfig); } pce->hMprConfig = NULL; pce->iscStatus = XORPRTM_STATUS_STOPPED; return NO_ERROR; }
int iokit_init(void) { int phase = 0; Semaphore handshake; log_debug("Initializing IOKit subsystem"); // create notification pipe if (pipe_create(&_notification_pipe) < 0) { log_error("Could not create notification pipe: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 1; if (event_add_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, iokit_forward_notifications, NULL) < 0) { goto cleanup; } phase = 2; // create notification poll thread if (semaphore_create(&handshake) < 0) { log_error("Could not create handshake semaphore: %s (%d)", get_errno_name(errno), errno); goto cleanup; } thread_create(&_poll_thread, iokit_poll_notifications, &handshake); semaphore_acquire(&handshake); semaphore_destroy(&handshake); phase = 3; if (!_running) { log_error("Could not start notification poll thread"); goto cleanup; } phase = 4; cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 3: thread_destroy(&_poll_thread); case 2: event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); case 1: pipe_destroy(&_notification_pipe); default: break; } return phase == 4 ? 0 : -1; }
int event_init_platform(void) { int phase = 0; // create read set if (event_reserve_socket_set(&_socket_read_set, 32) < 0) { log_error("Could not create socket read set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 1; // create write set if (event_reserve_socket_set(&_socket_write_set, 32) < 0) { log_error("Could not create socket write set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 2; // create error set if (event_reserve_socket_set(&_socket_error_set, 32) < 0) { log_error("Could not create socket error set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 3; // create stop pipe if (pipe_create(&_stop_pipe, 0) < 0) { log_error("Could not create stop pipe: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 4; if (event_add_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, NULL, NULL) < 0) { goto cleanup; } phase = 5; // create USB poll thread _usb_poll_running = false; _usb_poll_stuck = false; if (usbi_pipe(_usb_poll_suspend_pipe) < 0) { log_error("Could not create USB suspend pipe"); goto cleanup; } phase = 6; if (pipe_create(&_usb_poll_ready_pipe, 0) < 0) { log_error("Could not create USB ready pipe: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 7; if (array_create(&_usb_poll_pollfds, 32, sizeof(struct usbi_pollfd), true) < 0) { log_error("Could not create USB pollfd array: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 8; if (semaphore_create(&_usb_poll_resume) < 0) { log_error("Could not create USB resume semaphore: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 9; if (semaphore_create(&_usb_poll_suspend) < 0) { log_error("Could not create USB suspend semaphore: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 10; cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 9: semaphore_destroy(&_usb_poll_suspend); case 8: semaphore_destroy(&_usb_poll_resume); case 7: pipe_destroy(&_usb_poll_ready_pipe); case 6: usbi_close(_usb_poll_suspend_pipe[0]); usbi_close(_usb_poll_suspend_pipe[1]); case 5: event_remove_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); case 4: pipe_destroy(&_stop_pipe); case 3: free(_socket_error_set); case 2: free(_socket_write_set); case 1: free(_socket_read_set); default: break; } return phase == 10 ? 0 : -1; }
// NOTE: this function needs to call RegisterServiceCtrlHandlerEx and // SetServiceStatus in all circumstances if brickd is running as service static int generic_main(int log_to_file, int debug) { int exit_code = EXIT_FAILURE; const char *mutex_name = "Global\\Tinkerforge-Brick-Daemon-Single-Instance"; HANDLE mutex_handle = NULL; int mutex_error = 0; DWORD service_exit_code = NO_ERROR; int rc; char filename[1024]; int i; FILE *logfile = NULL; WSADATA wsa_data; DEV_BROADCAST_DEVICEINTERFACE notification_filter; HDEVNOTIFY notification_handle; mutex_handle = OpenMutex(SYNCHRONIZE, FALSE, mutex_name); if (mutex_handle == NULL) { rc = GetLastError(); if (rc == ERROR_ACCESS_DENIED) { rc = service_is_running(); if (rc < 0) { mutex_error = 1; // FIXME: set service_exit_code goto error_mutex; } else if (rc) { mutex_error = 1; service_exit_code = ERROR_SERVICE_ALREADY_RUNNING; log_error("Could not start as %s, another instance is already running as service", _run_as_service ? "service" : "console application"); goto error_mutex; } } if (rc != ERROR_FILE_NOT_FOUND) { mutex_error = 1; // FIXME: set service_exit_code rc += ERRNO_WINAPI_OFFSET; log_error("Could not open single instance mutex: %s (%d)", get_errno_name(rc), rc); goto error_mutex; } } if (mutex_handle != NULL) { mutex_error = 1; service_exit_code = ERROR_SERVICE_ALREADY_RUNNING; log_error("Could not start as %s, another instance is already running", _run_as_service ? "service" : "console application"); goto error_mutex; } mutex_handle = CreateMutex(NULL, FALSE, mutex_name); if (mutex_handle == NULL) { mutex_error = 1; // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not create single instance mutex: %s (%d)", get_errno_name(rc), rc); goto error_mutex; } if (!_run_as_service && !SetConsoleCtrlHandler(console_ctrl_handler, TRUE)) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not set console control handler: %s (%d)", get_errno_name(rc), rc); } if (log_to_file) { if (GetModuleFileName(NULL, filename, sizeof(filename)) == 0) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not get module file name: %s (%d)", get_errno_name(rc), rc); } else { i = strlen(filename); if (i < 4) { log_warn("Module file name '%s' is too short", filename); } else { strcpy(filename + i - 3, "log"); logfile = fopen(filename, "a+"); if (logfile == NULL) { log_warn("Could not open logfile '%s'", filename); } else { log_set_file(logfile); } } } } if (debug) { log_set_level(LOG_CATEGORY_EVENT, LOG_LEVEL_DEBUG); log_set_level(LOG_CATEGORY_USB, LOG_LEVEL_DEBUG); log_set_level(LOG_CATEGORY_NETWORK, LOG_LEVEL_DEBUG); log_set_level(LOG_CATEGORY_HOTPLUG, LOG_LEVEL_DEBUG); log_set_level(LOG_CATEGORY_OTHER, LOG_LEVEL_DEBUG); } else { log_set_level(LOG_CATEGORY_EVENT, config_get_log_level(LOG_CATEGORY_EVENT)); log_set_level(LOG_CATEGORY_USB, config_get_log_level(LOG_CATEGORY_USB)); log_set_level(LOG_CATEGORY_NETWORK, config_get_log_level(LOG_CATEGORY_NETWORK)); log_set_level(LOG_CATEGORY_HOTPLUG, config_get_log_level(LOG_CATEGORY_HOTPLUG)); log_set_level(LOG_CATEGORY_OTHER, config_get_log_level(LOG_CATEGORY_OTHER)); } if (_run_as_service) { log_info("Brick Daemon %s started (as service)", VERSION_STRING); } else { log_info("Brick Daemon %s started", VERSION_STRING); } if (config_has_error()) { log_warn("Errors found in config file '%s', run with --check-config option for details", _config_filename); } // initialize service status error_mutex: if (_run_as_service) { if (service_init(service_control_handler) < 0) { // FIXME: set service_exit_code goto error; } if (!mutex_error) { // service is starting service_set_status(SERVICE_START_PENDING, NO_ERROR); } } if (mutex_error) { goto error; } // initialize WinSock2 if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + WSAGetLastError(); log_error("Could not initialize Windows Sockets 2.2: %s (%d)", get_errno_name(rc), rc); goto error_event; } if (event_init() < 0) { // FIXME: set service_exit_code goto error_event; } if (usb_init() < 0) { // FIXME: set service_exit_code goto error_usb; } // create notification pipe if (pipe_create(_notification_pipe) < 0) { // FIXME: set service_exit_code log_error("Could not create notification pipe: %s (%d)", get_errno_name(errno), errno); goto error_pipe; } if (event_add_source(_notification_pipe[0], EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, forward_notifications, NULL) < 0) { // FIXME: set service_exit_code goto error_pipe_add; } // register device notification ZeroMemory(¬ification_filter, sizeof(notification_filter)); notification_filter.dbcc_size = sizeof(notification_filter); notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; if (_run_as_service) { notification_handle = RegisterDeviceNotification((HANDLE)service_get_status_handle(), ¬ification_filter, DEVICE_NOTIFY_SERVICE_HANDLE); } else { if (message_pump_start() < 0) { // FIXME: set service_exit_code goto error_pipe_add; } notification_handle = RegisterDeviceNotification(_message_pump_hwnd, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); } if (notification_handle == NULL) { // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not register for device notification: %s (%d)", get_errno_name(rc), rc); goto error_notification; } if (network_init() < 0) { // FIXME: set service_exit_code goto error_network; } // running if (_run_as_service) { service_set_status(SERVICE_RUNNING, NO_ERROR); } if (event_run() < 0) { // FIXME: set service_exit_code goto error_run; } exit_code = EXIT_SUCCESS; error_run: network_exit(); error_network: UnregisterDeviceNotification(notification_handle); error_notification: if (!_run_as_service) { message_pump_stop(); } event_remove_source(_notification_pipe[0], EVENT_SOURCE_TYPE_GENERIC); error_pipe_add: pipe_destroy(_notification_pipe); error_pipe: usb_exit(); error_usb: event_exit(); error_event: log_info("Brick Daemon %s stopped", VERSION_STRING); error: log_exit(); config_exit(); if (_run_as_service) { // because the service process can be terminated at any time after // entering SERVICE_STOPPED state the mutex is closed beforehand, // even though this creates a tiny time window in which the service // is still running but the mutex is not held anymore if (mutex_handle != NULL) { CloseHandle(mutex_handle); } // service is now stopped service_set_status(SERVICE_STOPPED, service_exit_code); } else { if (_pause_before_exit) { printf("Press any key to exit...\n"); getch(); } if (mutex_handle != NULL) { CloseHandle(mutex_handle); } } return exit_code; }
// NOTE: this function needs to call RegisterServiceCtrlHandlerEx and // SetServiceStatus in all circumstances if brickd is running as service static int generic_main(bool log_to_file, bool debug, bool libusb_debug) { int exit_code = EXIT_FAILURE; const char *mutex_name = "Global\\Tinkerforge-Brick-Daemon-Single-Instance"; HANDLE mutex_handle = NULL; bool fatal_error = false; DWORD service_exit_code = NO_ERROR; int rc; char filename[1024]; int i; FILE *log_file = NULL; WSADATA wsa_data; DEV_BROADCAST_DEVICEINTERFACE notification_filter; HDEVNOTIFY notification_handle; mutex_handle = OpenMutex(SYNCHRONIZE, FALSE, mutex_name); if (mutex_handle == NULL) { rc = GetLastError(); if (rc == ERROR_ACCESS_DENIED) { rc = service_is_running(); if (rc < 0) { fatal_error = true; // FIXME: set service_exit_code goto error_mutex; } else if (rc) { fatal_error = true; service_exit_code = ERROR_SERVICE_ALREADY_RUNNING; log_error("Could not start as %s, another instance is already running as service", _run_as_service ? "service" : "console application"); goto error_mutex; } } if (rc != ERROR_FILE_NOT_FOUND) { fatal_error = true; // FIXME: set service_exit_code rc += ERRNO_WINAPI_OFFSET; log_error("Could not open single instance mutex: %s (%d)", get_errno_name(rc), rc); goto error_mutex; } } if (mutex_handle != NULL) { fatal_error = true; service_exit_code = ERROR_SERVICE_ALREADY_RUNNING; log_error("Could not start as %s, another instance is already running", _run_as_service ? "service" : "console application"); goto error_mutex; } mutex_handle = CreateMutex(NULL, FALSE, mutex_name); if (mutex_handle == NULL) { fatal_error = true; // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not create single instance mutex: %s (%d)", get_errno_name(rc), rc); goto error_mutex; } if (log_to_file) { if (GetModuleFileName(NULL, filename, sizeof(filename)) == 0) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not get module file name: %s (%d)", get_errno_name(rc), rc); } else { i = strlen(filename); if (i < 4) { log_warn("Module file name '%s' is too short", filename); } else { filename[i - 3] = '\0'; string_append(filename, "log", sizeof(filename)); log_file = fopen(filename, "a+"); if (log_file == NULL) { log_warn("Could not open log file '%s'", filename); } else { printf("Logging to '%s'\n", filename); log_set_file(log_file); } } } } else if (_run_as_service) { log_set_file(NULL); } if (!_run_as_service && !SetConsoleCtrlHandler(console_ctrl_handler, TRUE)) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not set console control handler: %s (%d)", get_errno_name(rc), rc); } log_set_debug_override(debug); log_set_level(LOG_CATEGORY_EVENT, config_get_option("log_level.event")->value.log_level); log_set_level(LOG_CATEGORY_USB, config_get_option("log_level.usb")->value.log_level); log_set_level(LOG_CATEGORY_NETWORK, config_get_option("log_level.network")->value.log_level); log_set_level(LOG_CATEGORY_HOTPLUG, config_get_option("log_level.hotplug")->value.log_level); log_set_level(LOG_CATEGORY_HARDWARE, config_get_option("log_level.hardware")->value.log_level); log_set_level(LOG_CATEGORY_WEBSOCKET, config_get_option("log_level.websocket")->value.log_level); log_set_level(LOG_CATEGORY_OTHER, config_get_option("log_level.other")->value.log_level); if (config_has_error()) { log_error("Error(s) in config file '%s', run with --check-config option for details", _config_filename); fatal_error = true; goto error_config; } if (_run_as_service) { log_info("Brick Daemon %s started (as service)", VERSION_STRING); } else { log_info("Brick Daemon %s started", VERSION_STRING); } if (config_has_warning()) { log_warn("Warning(s) in config file '%s', run with --check-config option for details", _config_filename); } // initialize service status error_config: error_mutex: if (_run_as_service) { if (service_init(service_control_handler) < 0) { // FIXME: set service_exit_code goto error; } if (!fatal_error) { // service is starting service_set_status(SERVICE_START_PENDING, NO_ERROR); } } if (fatal_error) { goto error; } // initialize WinSock2 if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + WSAGetLastError(); log_error("Could not initialize Windows Sockets 2.2: %s (%d)", get_errno_name(rc), rc); goto error_event; } if (event_init() < 0) { // FIXME: set service_exit_code goto error_event; } if (hardware_init() < 0) { // FIXME: set service_exit_code goto error_hardware; } if (usb_init(libusb_debug) < 0) { // FIXME: set service_exit_code goto error_usb; } // create notification pipe if (pipe_create(&_notification_pipe) < 0) { // FIXME: set service_exit_code log_error("Could not create notification pipe: %s (%d)", get_errno_name(errno), errno); goto error_pipe; } if (event_add_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, forward_notifications, NULL) < 0) { // FIXME: set service_exit_code goto error_pipe_add; } // register device notification ZeroMemory(¬ification_filter, sizeof(notification_filter)); notification_filter.dbcc_size = sizeof(notification_filter); notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; if (_run_as_service) { notification_handle = RegisterDeviceNotification((HANDLE)service_get_status_handle(), ¬ification_filter, DEVICE_NOTIFY_SERVICE_HANDLE); } else { if (message_pump_start() < 0) { // FIXME: set service_exit_code goto error_pipe_add; } notification_handle = RegisterDeviceNotification(_message_pump_hwnd, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); } if (notification_handle == NULL) { // FIXME: set service_exit_code rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not register for device notification: %s (%d)", get_errno_name(rc), rc); goto error_notification; } if (network_init() < 0) { // FIXME: set service_exit_code goto error_network; } // running if (_run_as_service) { service_set_status(SERVICE_RUNNING, NO_ERROR); } if (event_run(network_cleanup_clients_and_zombies) < 0) { // FIXME: set service_exit_code goto error_run; } exit_code = EXIT_SUCCESS; error_run: network_exit(); error_network: UnregisterDeviceNotification(notification_handle); error_notification: if (!_run_as_service) { message_pump_stop(); } event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); error_pipe_add: pipe_destroy(&_notification_pipe); error_pipe: usb_exit(); error_usb: hardware_exit(); error_hardware: event_exit(); error_event: log_info("Brick Daemon %s stopped", VERSION_STRING); error: if (!_run_as_service) { // unregister the console handler before exiting the log. otherwise a // control event might be send to the control handler after the log // is not available anymore and the control handler tries to write a // log messages triggering a crash. this situation could easily be // created by clicking the close button of the command prompt window // while the getch call is waiting for the user to press a key. SetConsoleCtrlHandler(console_ctrl_handler, FALSE); } log_exit(); config_exit(); if (_run_as_service) { // because the service process can be terminated at any time after // entering SERVICE_STOPPED state the mutex is closed beforehand, // even though this creates a tiny time window in which the service // is still running but the mutex is not held anymore if (mutex_handle != NULL) { CloseHandle(mutex_handle); } // service is now stopped service_set_status(SERVICE_STOPPED, service_exit_code); } else { if (_pause_before_exit) { printf("Press any key to exit...\n"); getch(); } if (mutex_handle != NULL) { CloseHandle(mutex_handle); } } return exit_code; }
static void s_delete_pipe (void *argument) { pipe_t *pipe = (pipe_t *) argument; pipe_destroy (&pipe); }
// public API APIE process_spawn(ObjectID executable_id, ObjectID arguments_id, ObjectID environment_id, ObjectID working_directory_id, uint32_t uid, uint32_t gid, ObjectID stdin_id, ObjectID stdout_id, ObjectID stderr_id, Session *session, uint16_t object_create_flags, bool release_on_death, ProcessStateChangedFunction state_changed, void *opaque, ObjectID *id, Process **object) { int phase = 0; APIE error_code; String *executable; List *arguments; Array arguments_array; int i; char **item; List *environment; Array environment_array; String *working_directory; File *stdin; File *stdout; File *stderr; pid_t pid; int status_pipe[2]; int sc_open_max; FILE *log_file; Process *process; // acquire and lock executable string object error_code = string_get_acquired_and_locked(executable_id, &executable); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 1; if (*executable->buffer == '\0') { error_code = API_E_INVALID_PARAMETER; log_warn("Cannot spawn child process using empty executable name"); goto cleanup; } // lock arguments list object error_code = list_get_acquired_and_locked(arguments_id, OBJECT_TYPE_STRING, &arguments); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 2; // prepare arguments array for execvpe if (array_create(&arguments_array, 1 + arguments->items.count + 1, sizeof(char *), true) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not create arguments array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } phase = 3; item = array_append(&arguments_array); if (item == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } *item = executable->buffer; for (i = 0; i < arguments->items.count; ++i) { item = array_append(&arguments_array); if (item == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } *item = (*(String **)array_get(&arguments->items, i))->buffer; } item = array_append(&arguments_array); if (item == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } *item = NULL; // lock environment list object error_code = list_get_acquired_and_locked(environment_id, OBJECT_TYPE_STRING, &environment); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 4; // prepare environment array for execvpe if (array_create(&environment_array, environment->items.count + 1, sizeof(char *), true) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not create environment array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } phase = 5; for (i = 0; i < environment->items.count; ++i) { item = array_append(&environment_array); if (item == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to environment array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } // FIXME: if item is not <name>=<value>, but just <name> then use the parent <value> *item = (*(String **)array_get(&environment->items, i))->buffer; } item = array_append(&environment_array); if (item == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to environment array for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } *item = NULL; // acquire and lock working directory string object error_code = string_get_acquired_and_locked(working_directory_id, &working_directory); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 6; if (*working_directory->buffer == '\0') { error_code = API_E_INVALID_PARAMETER; log_warn("Cannot spawn child process (executable: %s) using empty working directory name", executable->buffer); goto cleanup; } if (*working_directory->buffer != '/') { error_code = API_E_INVALID_PARAMETER; log_warn("Cannot spawn child process (executable: %s) using working directory with relative name '%s'", executable->buffer, working_directory->buffer); goto cleanup; } // acquire stdin file object error_code = file_get_acquired(stdin_id, &stdin); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 7; // acquire stdout file object error_code = file_get_acquired(stdout_id, &stdout); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 8; // acquire stderr file object error_code = file_get_acquired(stderr_id, &stderr); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 9; // create status pipe if (pipe(status_pipe) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not create status pipe for spawning child process (executable: %s): %s (%d)", executable->buffer, get_errno_name(errno), errno); goto cleanup; } phase = 10; // fork log_debug("Forking to spawn child process (executable: %s)", executable->buffer); error_code = process_fork(&pid); if (error_code != API_E_SUCCESS) { goto cleanup; } if (pid == 0) { // child close(status_pipe[0]); // change user and groups error_code = process_set_identity(uid, gid); if (error_code != API_E_SUCCESS) { goto child_error; } // change directory if (chdir(working_directory->buffer) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not change directory to '%s' for child process (executable: %s, pid: %u): %s (%d)", working_directory->buffer, executable->buffer, getpid(), get_errno_name(errno), errno); goto child_error; } // get open FD limit sc_open_max = sysconf(_SC_OPEN_MAX); if (sc_open_max < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not get SC_OPEN_MAX value: %s (%d)", get_errno_name(errno), errno); goto child_error; } // redirect stdin if (dup2(file_get_read_handle(stdin), STDIN_FILENO) != STDIN_FILENO) { error_code = api_get_error_code_from_errno(); log_error("Could not redirect stdin for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, getpid(), get_errno_name(errno), errno); goto child_error; } // redirect stdout if (dup2(file_get_write_handle(stdout), STDOUT_FILENO) != STDOUT_FILENO) { error_code = api_get_error_code_from_errno(); log_error("Could not redirect stdout for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, getpid(), get_errno_name(errno), errno); goto child_error; } // stderr is the default log output in non-daemon mode. if this is // the case then disable the log output before redirecting stderr to // avoid polluting stderr for the new process log_file = log_get_file(); if (log_file != NULL && fileno(log_file) == STDERR_FILENO) { log_debug("Disable logging to stderr for child process (executable: %s, pid: %u)", executable->buffer, getpid()); log_set_file(NULL); } // redirect stderr if (dup2(file_get_write_handle(stderr), STDERR_FILENO) != STDERR_FILENO) { error_code = api_get_error_code_from_errno(); log_error("Could not redirect stderr for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, getpid(), get_errno_name(errno), errno); goto child_error; } // notify parent if (robust_write(status_pipe[1], &error_code, sizeof(error_code)) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not write to status pipe for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, getpid(), get_errno_name(errno), errno); goto child_error; } // disable log output. if stderr was not the current log output then // the log file is still open at this point. the next step is to close // all remaining file descriptors. just for good measure disable the // log output beforehand log_set_file(NULL); // close all file descriptors except the std* ones for (i = STDERR_FILENO + 1; i < sc_open_max; ++i) { close(i); } // execvpe only returns in case of an error execvpe(executable->buffer, (char **)arguments_array.bytes, (char **)environment_array.bytes); if (errno == ENOENT) { _exit(PROCESS_E_DOES_NOT_EXIST); } else { _exit(PROCESS_E_CANNOT_EXECUTE); } child_error: // notify parent in all cases if (robust_write(status_pipe[1], &error_code, sizeof(error_code)) < 0) { log_error("Could not write to status pipe for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, getpid(), get_errno_name(errno), errno); } close(status_pipe[1]); _exit(PROCESS_E_INTERNAL_ERROR); } phase = 11; // wait for child to start successfully if (robust_read(status_pipe[0], &error_code, sizeof(error_code)) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not read from status pipe for child process (executable: %s, pid: %u): %s (%d)", executable->buffer, pid, get_errno_name(errno), errno); goto cleanup; } if (error_code != API_E_SUCCESS) { goto cleanup; } // create process object process = calloc(1, sizeof(Process)); if (process == NULL) { error_code = API_E_NO_FREE_MEMORY; log_error("Could not allocate process object: %s (%d)", get_errno_name(ENOMEM), ENOMEM); goto cleanup; } phase = 12; // setup process object process->executable = executable; process->arguments = arguments; process->environment = environment; process->working_directory = working_directory; process->uid = uid; process->gid = gid; process->pid = pid; process->stdin = stdin; process->stdout = stdout; process->stderr = stderr; process->release_on_death = release_on_death; process->state_changed = state_changed; process->opaque = opaque; process->state = PROCESS_STATE_RUNNING; process->timestamp = time(NULL); process->exit_code = 0; // invalid if (pipe_create(&process->state_change_pipe, 0) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not create state change pipe child process (executable: %s, pid: %u): %s (%d)", executable->buffer, pid, get_errno_name(errno), errno); goto cleanup; } phase = 13; if (event_add_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, process_handle_state_change, process) < 0) { goto cleanup; } phase = 14; // create process object error_code = object_create(&process->base, OBJECT_TYPE_PROCESS, session, object_create_flags | OBJECT_CREATE_FLAG_INTERNAL, process_destroy, process_signature); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 15; if (id != NULL) { *id = process->base.id; } if (object != NULL) { *object = process; } // start thread to wait for child process state changes thread_create(&process->wait_thread, process_wait, process); log_debug("Spawned process object (id: %u, executable: %s, pid: %u)", process->base.id, executable->buffer, process->pid); close(status_pipe[0]); close(status_pipe[1]); array_destroy(&arguments_array, NULL); array_destroy(&environment_array, NULL); cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 14: event_remove_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); case 13: pipe_destroy(&process->state_change_pipe); case 12: free(process); case 11: kill(pid, SIGKILL); case 10: close(status_pipe[0]); close(status_pipe[1]); case 9: file_release(stderr); case 8: file_release(stdout); case 7: file_release(stdin); case 6: string_unlock_and_release(working_directory); case 5: array_destroy(&environment_array, NULL); case 4: list_unlock_and_release(environment); case 3: array_destroy(&arguments_array, NULL); case 2: list_unlock_and_release(arguments); case 1: string_unlock_and_release(executable); default: break; } return phase == 15 ? API_E_SUCCESS : error_code; }
/* * Create a new instance of a pipe and return a pointer to * its instance structure. */ pipe_instance_t * pipe_new(void) { pipe_instance_t *npp; int failed; DWORD result; TRACE0(ENTER, "Entering pipe_new"); npp = malloc(sizeof(*npp)); if (npp == NULL) return NULL; ZeroMemory(npp, sizeof(*npp)); failed = 1; /* XXX buffer management */ npp->rsize = PIPE_READBUF_SIZE; npp->state = PIPE_STATE_INIT; InitializeCriticalSection(&npp->rcs); /* * Create the event object used to signal connection completion. */ npp->cevent = CreateEvent(NULL, FALSE, FALSE, NULL); if (npp->cevent == NULL) { result = GetLastError(); TRACE1(CONFIGURATION, "Error %u creating event", result); goto fail; } npp->cov.hEvent = npp->cevent; /* * Create the event object used to signal read completion. */ npp->revent = CreateEvent(NULL, FALSE, FALSE, NULL); if (npp->revent == NULL) { result = GetLastError(); TRACE1(CONFIGURATION, "Error %u creating event", result); goto fail; } npp->rov.hEvent = npp->revent; /* * Create the instance of the named pipe itself. */ npp->pipe = CreateNamedPipeA(XORPRTM_PIPENAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE, PIPE_INSTANCES, 0, 0, XORPRTM_PIPETIMEOUT, NULL); if (npp->pipe == NULL) { result = GetLastError(); TRACE1(CONFIGURATION, "Error %u creating named pipe", result); goto fail; } failed = 0; fail: if (failed) { pipe_destroy(npp); npp = NULL; } TRACE1(ENTER, "Leaving pipe_new %p", npp); return (npp); }