Annotation of embedaddon/tmux/job.c, revision 1.1

1.1     ! misho       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>