aboutsummaryrefslogtreecommitdiff
path: root/src/command.c
blob: d44c773bfb7b97c1015947afb5fe455f9b758611 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "../include/command.h"
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#define READ_END 0
#define WRITE_END 1

int tsl_command_exec(char **args, ProgramOutputCallback output_callback, void *userdata) {
    int fd[2];
    pid_t pid;
    pid_t parent_pid = getpid();

    /* 1 arguments */
    if(args[0] == NULL)
        return -1;
    
    if(pipe(fd) == -1) {
        perror("Failed to open pipe");
        return -2;
    }

    pid = fork();
    if(pid == -1) {
        perror("Failed to fork");
        close(fd[READ_END]);
        close(fd[WRITE_END]);
        return -3;
    } else if(pid == 0) { /* child */
        if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
            perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed");
            exit(127);
        }

        /* Test if the parent died before the above call to prctl */
        if(getppid() != parent_pid)
            exit(127);

        dup2(fd[WRITE_END], STDOUT_FILENO);
        close(fd[READ_END]);
        close(fd[WRITE_END]);

        execvp(args[0], args);
        perror("execvp");
        exit(127);
    } else { /* parent */
        int result = 0;
        int status;
        char buffer[2048];
        int exit_status;

        close(fd[WRITE_END]);
        for(;;) {
            ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1);
            if(bytes_read == 0) {
                break;
            } else if(bytes_read == -1) {
                int err = errno;
                fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err));
                result = -err;
                goto cleanup;
            }

            buffer[bytes_read] = '\0';
            if(output_callback && !output_callback(buffer, bytes_read, userdata))
                break;
        }

        if(waitpid(pid, &status, 0) == -1) {
            perror("waitpid failed");
            result = -5;
            goto cleanup;
        }

        if(!WIFEXITED(status)) {
            result = -4;
            goto cleanup;
        }

        exit_status = WEXITSTATUS(status);
        if(exit_status != 0) {
            char **arg = args;
            fprintf(stderr, "Failed to execute program (");
            while(*arg) {
                if(arg != args)
                    fputc(' ', stderr);
                fprintf(stderr, "'%s'", *arg);
                ++arg;
            }
            fprintf(stderr, "), exit status %d\n", exit_status);
            result = -exit_status;
            goto cleanup;
        }

        cleanup:
        close(fd[READ_END]);
        return result;
    }
}