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); }
int fclose(FILE* stream) { _FILE* file = (_FILE*)stream; bool result = false; if (file == NULL) return EOF; if (close(file->fd) != 0) goto cleanup; file_release(file); result = true; cleanup: return result; }
static gboolean file_open (void *dp) { file_driver * const d = dp; AFfilesetup outfilesetup; outfilesetup = afNewFileSetup(); afInitFileFormat(outfilesetup, AF_FILE_WAVE); afInitChannels(outfilesetup, AF_DEFAULT_TRACK, 2); afInitSampleFormat(outfilesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16); d->outfile = afOpenFile(d->filename, "w", outfilesetup); afFreeFileSetup(outfilesetup); if(!d->outfile) { error_error(_("Can't open file for writing.")); goto out; } /* In case we're running setuid root... */ chown(d->filename, getuid(), getgid()); d->sndbuf_size = 16384; d->sndbuf = malloc(d->sndbuf_size); if(!d->sndbuf) { error_error("Can't allocate mix buffer."); goto out; } d->polltag = audio_poll_add(d->pipe[1], GDK_INPUT_WRITE, file_poll_ready_playing, d); d->firstpoll = TRUE; d->playtime = 0.0; return TRUE; out: file_release(dp); return FALSE; }
FILE* fopen(const char* filename, const char* mode) { bool result = false; bool mode_r = false; bool mode_w = false; bool mode_a = false; bool mode_r_plus = false; bool mode_w_plus = false; bool mode_a_plus = false; bool mode_t = false; bool mode_b = false; int num_rwa; int flags = 0; int pmode = 0; int fd = -1; _FILE* file = NULL; if (filename == NULL || mode == NULL) return NULL; file = file_allocate(); if (file == NULL) return NULL; while (*mode != 0) { switch (*mode) { case 'r': if (*(mode+1) == '+') { mode_r_plus = true; mode++; } else mode_r = true; break; case 'w': if (*(mode+1) == '+') { mode_w_plus = true; mode++; } else mode_w = true; break; case 'a': if (*(mode+1) == '+') { mode_a_plus = true; mode++; } else mode_a = true; break; case 't': mode_t = true; break; case 'b': mode_b = true; break; } mode++; } num_rwa = 0; if (mode_r) num_rwa++; if (mode_w) num_rwa++; if (mode_a) num_rwa++; if (mode_r_plus) num_rwa++; if (mode_w_plus) num_rwa++; if (mode_a_plus) num_rwa++; if (num_rwa != 1) goto cleanup; if (mode_t && mode_b) goto cleanup; // r = O_RDONLY // w = O_CREAT|O_TRUNC | O_WRONLY // a = O_CREAT | O_WRONLY | O_APPEND // r+ = O_RDWR // w+ = O_CREAT|O_TRUNC | O_RDWR // a+ = O_CREAT | O_RDWR | O_APPEND if (mode_w || mode_a || mode_w_plus || mode_a_plus) { flags |= O_CREAT; pmode = S_IREAD | S_IWRITE; } if (mode_w || mode_w_plus) flags |= O_TRUNC; if (mode_r) flags |= O_RDONLY; else if (mode_w || mode_a) flags |= O_WRONLY; else flags |= O_RDWR; if (mode_a || mode_a_plus) flags |= O_APPEND; if (mode_t) flags |= O_TEXT; if (mode_b) flags |= O_BINARY; fd = open(filename, flags, pmode); if (fd == -1) goto cleanup; file->fd = fd; file->bufferedChar = -1; file->error = FALSE; result = true; cleanup: if (result == false) { if (file != NULL) { file_release(file); file = NULL; // returned below } if (fd != -1) close(fd); } return (FILE*)file; }
static File *program_scheduler_prepare_continuous_log(ProgramScheduler *program_scheduler, struct timeval timestamp, const char *suffix) { struct tm localized_timestamp; char iso8601dt[64] = "unknown"; char iso8601usec[16] = ""; char iso8601tz[16] = ""; char buffer[1024]; APIE error_code; String *name; File *file; // format ISO 8601 date, time and timezone offset if (localtime_r(×tamp.tv_sec, &localized_timestamp) != NULL) { // can use common ISO 8601 format YYYY-MM-DDThh:mm:ss.uuuuuu±hhmm // because this timestamp is not part of a filename strftime(iso8601dt, sizeof(iso8601dt), "%Y-%m-%dT%H:%M:%S", &localized_timestamp); snprintf(iso8601usec, sizeof(iso8601usec), ".%06d", (int)timestamp.tv_usec); strftime(iso8601tz, sizeof(iso8601tz), "%z", &localized_timestamp); } // format log filename if (robust_snprintf(buffer, sizeof(buffer), "%s/continuous_%s.log", program_scheduler->log_directory, suffix) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format %s log file name: %s (%d)", suffix, get_errno_name(errno), errno); return NULL; } error_code = string_wrap(buffer, NULL, OBJECT_CREATE_FLAG_INTERNAL | OBJECT_CREATE_FLAG_LOCKED, NULL, &name); if (error_code != API_E_SUCCESS) { program_scheduler_handle_error(program_scheduler, true, "Could not wrap %s log file name into string object: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } // open log file error_code = file_open(name->base.id, FILE_FLAG_WRITE_ONLY | FILE_FLAG_CREATE | FILE_FLAG_APPEND, 0644, 1000, 1000, NULL, OBJECT_CREATE_FLAG_INTERNAL, NULL, &file); string_unlock_and_release(name); if (error_code != API_E_SUCCESS) { program_scheduler_handle_error(program_scheduler, true, "Could not open/create %s log file: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } // format header if (robust_snprintf(buffer, sizeof(buffer), "\n\n%s%s%s\n-------------------------------------------------------------------------------\n", iso8601dt, iso8601usec, iso8601tz) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format timestamp for %s log file: %s (%d)", suffix, get_errno_name(errno), errno); file_release(file); return NULL; } // write header if (write(file->fd, buffer, strlen(buffer)) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not write timestamp to %s log file: %s (%d)", suffix, get_errno_name(errno), errno); file_release(file); return NULL; } return file; }
// 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; }
/* --------------------------------------- 文件处理回调 --------------------------------------- */ static bool_t hasher (void_t *param, sSEARCHa *info) { sHASH hash; /* 过滤掉两个生成的文件 */ if (str_cmpA(info->name, "__hash__.old") == 0 || str_cmpA(info->name, "__hash__.txt") == 0) return (TRUE); /* 显示文件名和大小字节数 */ printf("%s (%" CR_FSZ "u Bytes) ", info->name, info->size); /* 根据内存大小选择读取方式 */ timer_set_base(s_profile); if (info->size == 0) { /* 空文件 */ hash_init(&hash); hash_update(&hash, NULL, 0); hash_finish(param, info->size, info->name, &hash); } else if (info->size <= s_total) { sVFILE file; void_t *data; /* 内存映射 */ data = file_mappingA(info->name, &file); if (data == NULL) goto _read_it; hash_init(&hash); hash_update(&hash, data, (leng_t)info->size); hash_finish(param, info->size, info->name, &hash); file_release(&file); } else { fraw_t file; leng_t rest; fsize_t blks; _read_it: /* 分块读取 */ file = file_raw_openA(info->name, CR_FO_RO | CR_FO_SEQ); if (file == NULL) goto _failure; /* 文件很大, 只能分块读取 */ if (s_rdata == NULL) s_rdata = mem_malloc(FREAD_BLOCK); rest = ( leng_t)(info->size % FREAD_BLOCK); blks = (fsize_t)(info->size / FREAD_BLOCK); for (hash_init(&hash); blks != 0; blks--) { if (file_raw_read(s_rdata, FREAD_BLOCK, file) != FREAD_BLOCK) { file_raw_close(file); goto _failure; } hash_update(&hash, s_rdata, FREAD_BLOCK); } if (rest != 0) { if (file_raw_read(s_rdata, rest, file) != rest) { file_raw_close(file); goto _failure; } hash_update(&hash, s_rdata, rest); } hash_finish(param, info->size, info->name, &hash); file_raw_close(file); } fp32_t time; time = timer_get_delta(s_profile); time *= 1.024f; printf(" %.2f KB/S\n", info->size / time); return (TRUE); _failure: printf(" [FAILED]\n"); return (TRUE); }