File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / tmux / job.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:22:44 2017 UTC (6 years, 11 months ago) by misho
Branches: tmux, MAIN
CVS tags: v2_4p0, v2_4, HEAD
tmux 2.4

    1: /* $OpenBSD$ */
    2: 
    3: /*
    4:  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
    5:  *
    6:  * Permission to use, copy, modify, and distribute this software for any
    7:  * purpose with or without fee is hereby granted, provided that the above
    8:  * copyright notice and this permission notice appear in all copies.
    9:  *
   10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
   15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
   16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17:  */
   18: 
   19: #include <sys/types.h>
   20: #include <sys/socket.h>
   21: 
   22: #include <fcntl.h>
   23: #include <signal.h>
   24: #include <stdlib.h>
   25: #include <string.h>
   26: #include <unistd.h>
   27: 
   28: #include "tmux.h"
   29: 
   30: /*
   31:  * Job scheduling. Run queued commands in the background and record their
   32:  * output.
   33:  */
   34: 
   35: static void	job_callback(struct bufferevent *, short, void *);
   36: static void	job_write_callback(struct bufferevent *, void *);
   37: 
   38: /* All jobs list. */
   39: struct joblist	all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
   40: 
   41: /* Start a job running, if it isn't already. */
   42: struct job *
   43: job_run(const char *cmd, struct session *s, const char *cwd,
   44:     void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
   45: {
   46: 	struct job	*job;
   47: 	struct environ	*env;
   48: 	pid_t		 pid;
   49: 	int		 nullfd, out[2];
   50: 	const char	*home;
   51: 
   52: 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
   53: 		return (NULL);
   54: 
   55: 	env = environ_for_session(s);
   56: 	switch (pid = fork()) {
   57: 	case -1:
   58: 		environ_free(env);
   59: 		close(out[0]);
   60: 		close(out[1]);
   61: 		return (NULL);
   62: 	case 0:		/* child */
   63: 		clear_signals(1);
   64: 
   65: 		if (cwd == NULL || chdir(cwd) != 0) {
   66: 			if ((home = find_home()) == NULL || chdir(home) != 0)
   67: 				chdir("/");
   68: 		}
   69: 
   70: 		environ_push(env);
   71: 		environ_free(env);
   72: 
   73: 		if (dup2(out[1], STDIN_FILENO) == -1)
   74: 			fatal("dup2 failed");
   75: 		if (dup2(out[1], STDOUT_FILENO) == -1)
   76: 			fatal("dup2 failed");
   77: 		if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
   78: 			close(out[1]);
   79: 		close(out[0]);
   80: 
   81: 		nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
   82: 		if (nullfd < 0)
   83: 			fatal("open failed");
   84: 		if (dup2(nullfd, STDERR_FILENO) == -1)
   85: 			fatal("dup2 failed");
   86: 		if (nullfd != STDERR_FILENO)
   87: 			close(nullfd);
   88: 
   89: 		closefrom(STDERR_FILENO + 1);
   90: 
   91: 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
   92: 		fatal("execl failed");
   93: 	}
   94: 
   95: 	/* parent */
   96: 	environ_free(env);
   97: 	close(out[1]);
   98: 
   99: 	job = xmalloc(sizeof *job);
  100: 	job->state = JOB_RUNNING;
  101: 
  102: 	job->cmd = xstrdup(cmd);
  103: 	job->pid = pid;
  104: 	job->status = 0;
  105: 
  106: 	LIST_INSERT_HEAD(&all_jobs, job, lentry);
  107: 
  108: 	job->callbackfn = callbackfn;
  109: 	job->freefn = freefn;
  110: 	job->data = data;
  111: 
  112: 	job->fd = out[0];
  113: 	setblocking(job->fd, 0);
  114: 
  115: 	job->event = bufferevent_new(job->fd, NULL, job_write_callback,
  116: 	    job_callback, job);
  117: 	bufferevent_enable(job->event, EV_READ|EV_WRITE);
  118: 
  119: 	log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
  120: 	return (job);
  121: }
  122: 
  123: /* Kill and free an individual job. */
  124: void
  125: job_free(struct job *job)
  126: {
  127: 	log_debug("free job %p: %s", job, job->cmd);
  128: 
  129: 	LIST_REMOVE(job, lentry);
  130: 	free(job->cmd);
  131: 
  132: 	if (job->freefn != NULL && job->data != NULL)
  133: 		job->freefn(job->data);
  134: 
  135: 	if (job->pid != -1)
  136: 		kill(job->pid, SIGTERM);
  137: 	if (job->event != NULL)
  138: 		bufferevent_free(job->event);
  139: 	if (job->fd != -1)
  140: 		close(job->fd);
  141: 
  142: 	free(job);
  143: }
  144: 
  145: /* Called when output buffer falls below low watermark (default is 0). */
  146: static void
  147: job_write_callback(__unused struct bufferevent *bufev, void *data)
  148: {
  149: 	struct job	*job = data;
  150: 	size_t		 len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event));
  151: 
  152: 	log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd,
  153: 	    (long) job->pid, len);
  154: 
  155: 	if (len == 0) {
  156: 		shutdown(job->fd, SHUT_WR);
  157: 		bufferevent_disable(job->event, EV_WRITE);
  158: 	}
  159: }
  160: 
  161: /* Job buffer error callback. */
  162: static void
  163: job_callback(__unused struct bufferevent *bufev, __unused short events,
  164:     void *data)
  165: {
  166: 	struct job	*job = data;
  167: 
  168: 	log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
  169: 
  170: 	if (job->state == JOB_DEAD) {
  171: 		if (job->callbackfn != NULL)
  172: 			job->callbackfn(job);
  173: 		job_free(job);
  174: 	} else {
  175: 		bufferevent_disable(job->event, EV_READ);
  176: 		job->state = JOB_CLOSED;
  177: 	}
  178: }
  179: 
  180: /* Job died (waitpid() returned its pid). */
  181: void
  182: job_died(struct job *job, int status)
  183: {
  184: 	log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid);
  185: 
  186: 	job->status = status;
  187: 
  188: 	if (job->state == JOB_CLOSED) {
  189: 		if (job->callbackfn != NULL)
  190: 			job->callbackfn(job);
  191: 		job_free(job);
  192: 	} else {
  193: 		job->pid = -1;
  194: 		job->state = JOB_DEAD;
  195: 	}
  196: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>