Added #:redirect option to manifest.scm
[tlb/tomd.git] / src / guile_helpers.c
1 /* Copyright (C) 2018 Thomas Balzer */
2
3 /* This file is part of tomd. */
4
5 /* tomd is free software: you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation, either version 3 of the License, or */
8 /* (at your option) any later version. */
9
10 /* tomd is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU General Public License for more details. */
14
15 /* You should have received a copy of the GNU General Public License */
16 /* along with tomd. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <pwd.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <sys/wait.h>
24
25 #include "../include/job.h"
26 #include "../include/macros.h"
27
28 #ifdef GUILE_CAPABLE
29 #include <libguile.h>
30 #include "../include/scm_interface.h"
31 #endif
32
33 #define MANIFEST_LOC "/.config/tomd/init/manifest.scm"
34
35 #define MAX_JOBS 10
36
37 static int root_job = -1;
38
39 #ifdef GUILE_CAPABLE
40 static struct job jobs[MAX_JOBS] =
41 {
42 {
43 "bash", /* name */
44 "/bin/bash", /* cmd */
45 {}, /* args */
46 0, /* pid */
47 0, /* last_status */
48 0, /* redirect */
49 1, /* root */
50 },
51 /* rest null */
52 {}
53 };
54 #else
55 static struct job jobs[MAX_JOBS] = {};
56 #endif
57
58 #ifdef GUILE_CAPABLE
59 static struct job load_job(SCM job_list, int index)
60 {
61 struct job ret = { NULL };
62 SCM scm_cur_job = SCM_ARR(job_list, index);
63
64 if(scm_is_false(job_predicate(scm_cur_job))){
65 tomd_p("job %d wasn't a real job type.", index);
66 return ret;
67 }
68
69 SCM scm_name = get_name(scm_cur_job);
70 SCM scm_cmd = get_cmd(scm_cur_job);
71 SCM scm_args = get_args(scm_cur_job);
72 SCM scm_redirect = get_redirect(scm_cur_job);
73
74 /* TODO > Handle these. */
75 /* planned is to be able to run at different points, and at */
76 /* the request of a user. for testing we do things all at */
77 /* boot up. */
78 SCM scm_start_trigger = get_start_trigger(scm_cur_job);
79 SCM scm_end_trigger = get_end_trigger(scm_cur_job);
80
81 bool redirect = scm_to_bool(scm_redirect);
82 char *job_name = scm_to_locale_string(scm_name);
83 char *job_cmd = scm_to_locale_string(scm_cmd);
84 char *real_args[10];
85 int jlen = scm_to_int(scm_length(scm_args));
86 for(int j = 0;
87 j < jlen;
88 j++){
89 real_args[j] =
90 scm_to_locale_string(SCM_ARR(scm_args, j));
91 }
92
93 ret.name = job_name;
94 ret.cmd = job_cmd;
95 ret.redirect = redirect;
96
97 for(int j = 1;
98 j <= jlen;
99 j++){
100 ret.args[j] = real_args[j - 1];
101 }
102 ret.args[jlen + 1] = NULL;
103 ret.args[0] = ret.cmd;
104
105 return ret;
106 }
107
108 static void *load_manifest(void *args)
109 {
110 char *manifest_loc;
111 if(!args){
112 tomd_p("arg to load_manifest is NULL");
113 exit(EXIT_FAILURE);
114 }
115 manifest_loc = (char *)args;
116
117 scm_c_primitive_load(args);
118
119 SCM scm_job_list =
120 scm_c_public_ref("tomd manifest", "job-list");
121
122 if(scm_is_false(scm_job_list)){
123 tomd_p("no job-list found in manifest.scm");
124 return NULL;
125 }
126
127 if(scm_is_false(scm_list_p(scm_job_list))){
128 tomd_p("job-list found, but isn't a list.");
129 return NULL;
130 }
131
132 int i;
133 int len = SCM_LIST_LEN(scm_job_list);
134 tomd_p("len=%d, max=%d", len, MAX_JOBS);
135 for(i = 0;
136 i < len && i < MAX_JOBS;
137 i++){
138 jobs[i] = load_job(scm_job_list, i);
139 }
140 tomd_p("looked at %d jobs.", i);
141 jobs[i].cmd = NULL;
142 }
143 #endif
144
145 static char *lookup_user_folder(void)
146 {
147 #ifdef GUILE_CAPABLE
148 uid_t user = getuid();
149 struct passwd *userpasswd =
150 getpwuid(user);
151
152 /* take the manifest location and sub ~ for pw_dir */
153 char *buffer = (char *)malloc(sizeof(char) * 300);
154 strcpy(buffer, userpasswd->pw_dir);
155 int len = strlen(userpasswd->pw_dir);
156 strcpy(buffer + len, MANIFEST_LOC);
157 return buffer;
158 #else
159 return NULL;
160 #endif
161 }
162
163 void load_jobs(void)
164 {
165 char *manifest = lookup_user_folder();
166 #ifdef GUILE_CAPABLE
167 tomd_p("Loading jobs from '%s'", manifest);
168
169 void *res =
170 scm_with_guile(load_manifest,
171 (void *) manifest);
172 #endif
173 tomd_p("Stubbed job loading.");
174
175 tomd_p("Finished loading.");
176
177 int i = 0;
178 while(1){
179 if(i >= MAX_JOBS ||
180 jobs[i].cmd == NULL) {
181 break;
182 }
183 tomd_p("JOB <%d>:", i);
184 tomd_p(" cmd:'%s'", jobs[i].cmd);
185 int j = 1;
186 while(1){
187 if(i >= 10 ||
188 jobs[i].args[j] == NULL){
189 break;
190 }
191 tomd_p("arg [%d]:'%s'", j, jobs[i].args[j]);
192 j++;
193 }
194 i++;
195 }
196 }
197
198 static int stdincache;
199 static int stdoutcache;
200 static int stderrcache;
201
202 void silent(void)
203 {
204 /* cache all filedes */
205 stdincache = dup(STDIN_FILENO);
206 stdoutcache = dup(STDOUT_FILENO);
207 stderrcache = dup(STDERR_FILENO);
208
209 tomd_p("going silent.");
210
211 /* close default filedes */
212 close(STDIN_FILENO);
213 close(STDOUT_FILENO);
214 close(STDERR_FILENO);
215
216 tomd_p("now silent.");
217 }
218
219 #define LOG_DIR "/var/log/tomd/"
220
221 void run_job(int index)
222 {
223 struct job *job = &jobs[index];
224
225 pid_t pid = fork();
226 if(pid == 0){ /* child */
227 if(job->redirect){
228 /* redirect to a file */
229 char buf[100];
230 strcpy(buf, LOG_DIR);
231 strcpy(buf + strlen(LOG_DIR), job->name);
232 tomd_p("redirecting stdout to %s.", buf);
233 if(unlink(buf) != 0){
234 tomd_p("file couldn't be removed.");
235 }
236
237 int fd = open(buf, O_WRONLY | O_CREAT, 0644);
238 if(fd < 0){
239 tomd_p("couldn't open file.");
240 return;
241 }
242
243 dup2(fd, STDOUT_FILENO);
244 dup2(fd, STDERR_FILENO);
245 close(fd);
246 }
247 execvp(job->cmd, job->args);
248 tomd_panic("execvp for '%s' failed", job->cmd);
249 }else{ /* parent */
250 tomd_p("forked [%d] to run '%s'", pid, job->cmd);
251 job->pid = pid;
252 waitpid(job->pid, &job->last_status, WNOHANG);
253 }
254 }
255
256 struct job *lookup_job(char *my_job_name)
257 {
258 for(int i = 0;
259 i < MAX_JOBS;
260 i++){
261 if(jobs[i].name == NULL){
262 continue;
263 }
264 if(strcmp(jobs[i].name, my_job_name) == 0){
265 return &jobs[i];
266 }
267 }
268 return NULL;
269 }
270
271 void run_jobs(void)
272 {
273 int i;
274 for(i = 0;
275 i < MAX_JOBS;
276 i++){
277 if(jobs[i].cmd == NULL){
278 tomd_p("out of jobs");
279 return;
280 }else{
281 tomd_p("running job [%d] '%s'", i, jobs[i].cmd);
282 run_job(i);
283 if(jobs[i].root){
284 if(root_job != i && root_job != -1){
285 tomd_p("error! only one job can be root.");
286 }else{
287 root_job = i;
288 }
289 }
290 }
291 }
292 }
293
294 static int delay = 0;
295
296 void check_root_job(void)
297 {
298 delay++;
299 if(delay % 100 != 0){
300 return;
301 }else{
302 delay = 0;
303 }
304 if(root_job != -1){
305 int n_pid = waitpid(jobs[root_job].pid, &jobs[root_job].last_status, WNOHANG);
306 if(n_pid != jobs[root_job].pid){
307 return;
308 }
309
310 if(WIFEXITED(jobs[root_job].last_status)){
311 /* we died, restart it */
312 tomd_p("restarting root job. (last pid = %d, last status = %d)",
313 jobs[root_job].pid, jobs[root_job].last_status);
314 run_job(root_job);
315 }
316 }
317 }