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
106
107
108
109
110
111
112
113
114
115
116
117
|
/**
* @file shell.c
*/
#include "spm.h"
/**
* A wrapper for `popen()` that executes non-interactive programs and reports their exit value.
* To redirect stdout and stderr you must do so from within the `fmt` string
*
* ~~~{.c}
* int fd = 1; // stdout
* const char *log_file = "log.txt";
* Process *proc_info;
* int status;
*
* // Send stderr to stdout
* shell(&proc_info, SHELL_OUTPUT, "foo 2>&1");
* // Send stdout and stderr to /dev/null
* shell(&proc_info, SHELL_OUTPUT,"bar &>/dev/null");
* // Send stdout from baz to log.txt
* shell(&proc_info, SHELL_OUTPUT, "baz %d>%s", fd, log_file);
* // Do not record or redirect output from any streams
* shell(&proc_info, SHELL_DEFAULT, "biff");
* ~~~
*
* @param Process uninitialized `Process` struct will be populated with process data
* @param options change behavior of the function
* @param fmt shell command to execute (accepts `printf` style formatters)
* @param ... variadic arguments (used by `fmt`)
*/
void shell(Process **proc_info, u_int64_t option, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
clockid_t clockid = CLOCK_REALTIME;
struct timespec start, stop;
FILE *proc = NULL;
(*proc_info) = (Process *)calloc(1, sizeof(Process));
if (!(*proc_info)) {
fprintf(SYSERROR);
exit(errno);
}
(*proc_info)->returncode = -1;
// outbuf needs to be an integer type because fgetc returns EOF (> char)
int *outbuf = (int *)calloc(1, sizeof(int));
if (!outbuf) {
fprintf(SYSERROR);
exit(errno);
}
char *cmd = (char *)calloc(PATH_MAX, sizeof(char));
if (!cmd) {
fprintf(SYSERROR);
exit(errno);
}
vsnprintf(cmd, PATH_MAX, fmt, args);
if (option & SHELL_BENCHMARK) {
if (clock_gettime(clockid, &start) < 0) {
perror("clock_gettime");
exit(errno);
}
}
proc = popen(cmd, "r");
if (!proc) {
free(outbuf);
va_end(args);
return;
}
if (option & SHELL_OUTPUT) {
size_t bytes_read = 0;
size_t i = 0;
size_t new_buf_size;
(*proc_info)->output = (char *)calloc(BUFSIZ, sizeof(char));
while ((*outbuf = fgetc(proc)) != EOF) {
if (i >= BUFSIZ) {
new_buf_size = BUFSIZ + (i + bytes_read);
(*proc_info)->output = (char *)realloc((*proc_info)->output, new_buf_size);
i = 0;
}
if (*outbuf) {
(*proc_info)->output[bytes_read] = (char)*outbuf;
}
bytes_read++;
i++;
}
}
(*proc_info)->returncode = pclose(proc);
if (option & SHELL_BENCHMARK) {
if (clock_gettime(clockid, &stop) < 0) {
perror("clock_gettime");
exit(errno);
}
(*proc_info)->time_elapsed = (double)(stop.tv_sec - start.tv_sec) + (double)(stop.tv_nsec - start.tv_nsec) / 1E9;;
}
va_end(args);
free(outbuf);
free(cmd);
}
/**
* Free process resources allocated by `shell()`
* @param proc_info `Process` struct
*/
void shell_free(Process *proc_info) {
if (proc_info->output) {
free(proc_info->output);
}
free(proc_info);
}
|