Example #1
0
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);
}
Example #2
0
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;
}
Example #4
0
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(&timestamp.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;
}
Example #6
0
// 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;
}
Example #7
0
/*
---------------------------------------
    文件处理回调
---------------------------------------
*/
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);
}