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