]> granicus.if.org Git - fcron/blob - fcrondyn_svr.c
added runatresume / @resume
[fcron] / fcrondyn_svr.c
1 /*
2  * FCRON - periodic command scheduler 
3  *
4  *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  * 
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  *  The GNU General Public License can also be found in the file
21  *  `LICENSE' that comes with the fcron source distribution.
22  */
23
24
25 /* This file contains all fcron's code (server) to handle communication with fcrondyn */
26
27 #include "fcron.h"
28 #include "fcrondyn_svr.h"
29 #include "select.h"
30 #include "getloadavg.h"
31 #include "database.h"
32 #include "fcronconf.h"
33
34
35 void remove_connection(struct fcrondyn_cl **client,
36                        struct fcrondyn_cl *prev_client, select_instance * si);
37 void exe_cmd(struct fcrondyn_cl *client);
38 #ifdef SO_PEERCRED              /* linux */
39 void auth_client_so_peercred(struct fcrondyn_cl *client);
40 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)    /* resp. solaris 10 and Free/OpenBSD) */
41 void auth_client_getpeer(struct fcrondyn_cl *client);
42 #endif
43 void auth_client_password(struct fcrondyn_cl *client);
44 void cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root);
45 void print_fields(int fd, unsigned char *details);
46 void print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid,
47                 int index, time_t until);
48 void cmd_on_exeq(struct fcrondyn_cl *client, long int *cmd, int fd,
49                  int is_root);
50 void cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
51                 int is_root);
52 void cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd,
53                      exe_t * e);
54 void cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root);
55
56 fcrondyn_cl *fcrondyn_cl_base;  /* list of connected fcrondyn clients */
57 int fcrondyn_cl_num = 0;        /* number of fcrondyn clients currently connected */
58 int listen_fd = -1;             /* fd which catches incoming connection */
59 int auth_fail = 0;              /* number of auth failure */
60 time_t auth_nofail_since = 0;   /* we refuse auth since x due to too many failures */
61
62 /* some error messages ... */
63 char err_no_err_str[] = "Command successfully completed.\n";
64 char err_unknown_str[] =
65     "Fcron has encountered an error : command not completed.\n";
66 char err_cmd_unknown_str[] = "Unknown command.\n";
67 char err_job_nfound_str[] = "No corresponding job found.\n";
68 char err_rjob_nfound_str[] =
69     "No corresponding running job found.\n (The job may have "
70     "just finished its execution.)\n";
71 char err_invalid_user_str[] = "Invalid user : unable to find a passwd entry.\n";
72 char err_invalid_args_str[] = "Invalid arguments.\n";
73 char err_job_nallowed_str[] = "You are not allowed to see/change this line.\n";
74 char err_all_nallowed_str[] = "You are not allowed to list all jobs.\n";
75 char err_others_nallowed_str[] =
76     "You are not allowed to list other users' jobs.\n";
77
78 /* Send an error message to fcrondyn */
79 #define Send_err_msg(FD, MSG) \
80           send(FD, MSG, sizeof(MSG), 0);
81
82 /* to tell fcrondyn there's no more data to wait */
83 #define Tell_no_more_data(FD) \
84             send(FD, END_STR, sizeof(END_STR), 0);
85
86 /* Send an error message to fcrondyn and return */
87 #define Send_err_msg_end(FD, MSG) \
88         { \
89           send(FD, MSG, sizeof(MSG), 0); \
90           Tell_no_more_data(FD); \
91         }
92
93 /* which bit corresponds to which field ? */
94 #define FIELD_USER 0
95 #define FIELD_RQ 1
96 #define FIELD_PID 2
97 #define FIELD_SCHEDULE 3
98 #define FIELD_LAVG 4
99 #define FIELD_INDEX 5
100 #define FIELD_OPTIONS 6
101
102 /* the number of char we need (8 bits (i.e. fields) per char) */
103 #define FIELD_NUM_SIZE 1
104
105
106 void
107 fcrondyn_socket_init(select_instance * si)
108     /* do everything needed to get a working listening socket */
109 {
110     struct sockaddr_un addr;
111     int len = 0;
112     int sun_len = 0;
113
114     if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
115         error_e("Could not create socket : fcrondyn won't work");
116         return;
117     }
118
119     addr.sun_family = AF_UNIX;
120     len = strlen(fifofile);
121     if (len > sizeof(addr.sun_path) - 1) {
122         error("Error : fifo file path too long (max is %d)",
123               sizeof(addr.sun_path) - 1);
124         goto err;
125     }
126     strncpy(addr.sun_path, fifofile, sizeof(addr.sun_path));
127     addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
128     sun_len = (addr.sun_path - (char *)&addr) + len;
129 #if HAVE_SA_LEN
130     addr.sun_len = sun_len;
131 #endif
132
133     unlink(fifofile);
134     if (bind(listen_fd, (struct sockaddr *)&addr, sun_len) != 0) {
135         error_e("Cannot bind socket to '%s'", fifofile);
136         goto err;
137     }
138
139     if (listen(listen_fd, MAX_CONNECTION) != 0) {
140         error_e("Cannot set socket in listen mode");
141         goto err;
142     }
143
144     /* */
145     /* The exec bit is not necessary and ignored on all systems but AIX, where it is
146      * needed to allow fcrondyn to open the file */
147     if (chmod
148         (fifofile,
149          S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH |
150          S_IWOTH | S_IXOTH) != 0)
151         error_e("Cannot chmod() socket file");
152     /* */
153
154     fcntl(listen_fd, F_SETFD, 1);
155     /* set listen_fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
156     if (fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL) | O_NONBLOCK) == -1) {
157         error_e
158             ("Could not set listen_fd attribute O_NONBLOCK : no fcrondyn support");
159         goto err;
160     }
161
162     /* no error */
163     select_add_read(si, listen_fd);
164
165     debug("Socket initialized : listen_fd : %d", listen_fd);
166     return;
167
168  err:
169     fcrondyn_socket_close(si);
170
171 }
172
173 #if defined(HAVE_GETPEERUCRED)
174 void
175 auth_client_getpeer(struct fcrondyn_cl *client)
176     /* check client identity by reading its credentials from the socket
177      * using getpeerucred() (Solaris 10 onward).
178      * Sets client->fcl_user on success, don't do anything on failure
179      * so that the client stays unauthenticated */
180 {
181     struct passwd *p_entry = NULL;
182     ucred_t *ucred = NULL;
183     uid_t uid = -1;
184
185     if (getpeerucred(client->fcl_sock_fd, &ucred) < 0) {
186         error_e("Could not get client credentials using getpeerucred()");
187         return;
188     }
189     uid = ucred_getruid(ucred);
190     if (uid == -1) {
191         error_e("Could not get client uid from ucred_t");
192         return;
193     }
194     p_entry = getpwuid(uid);
195     if (p_entry == NULL) {
196         error_e("Could not find password entry for uid %d", uid);
197         return;
198     }
199
200     /* Successfully identified user: */
201     client->fcl_user = strdup2(p_entry->pw_name);
202
203     explain("Client's pid=%d, uid=%d, username=%s",
204             ucred_getpid(ucred), uid, client->fcl_user);
205
206 }
207 #endif                          /* HAVE_GETPEERUCRED */
208
209 #if defined(HAVE_GETPEEREID)
210 /*
211  * WARNING: UNTESTED CODE !!!
212  */
213 void
214 auth_client_getpeer(struct fcrondyn_cl *client)
215     /* check client identity by reading its credentials from the socket
216      * using getpeerucred() (Solaris 10 onward) or getpeereid(open/freeBSD).
217      * Sets client->fcl_user on success, don't do anything on failure
218      * so that the client stays unauthenticated */
219 {
220     struct passwd *p_entry = NULL;
221     uid_t euid = -1;
222     gid_t egid = -1;
223
224     if (getpeereid(client->fcl_sock_fd, &euid, &egid) < 0) {
225         error_e("Could not get client credentials using getpeereid()");
226         return;
227     }
228     p_entry = getpwuid(euid);
229     if (p_entry == NULL) {
230         error_e("Could not find password entry for uid %d", euid);
231         return;
232     }
233
234     /* Successfully identified user: */
235     client->fcl_user = strdup2(p_entry->pw_name);
236
237     explain("Client's uid=%d, gid=%d username=%s", euid, egid,
238             client->fcl_user);
239
240 }
241 #endif                          /* HAVE_GETPEEREID */
242
243
244
245 #ifdef SO_PEERCRED
246 void
247 auth_client_so_peercred(struct fcrondyn_cl *client)
248     /* check client identity by reading its credentials from the socket
249      * using getsockopt(SO_PEERCRED) (Linux)
250      * Sets client->fcl_user on success, don't do anything on failure
251      * so that the client stays unauthenticated */
252 {
253     const int true = 1;
254     /* There is no ucred.h (or equivalent) on linux to define struct ucred (!!)
255      * so we do it here */
256 #if ! ( defined(HAVE_CRED_H) && defined(HAVE_UCRED_H) \
257                 && defined(HAVE_SYS_CRED_H) && defined(HAVE_SYS_UCRED_H) )
258     struct ucred {
259         pid_t pid;
260         uid_t uid;
261         gid_t gid;
262     };
263 #endif                          /* struct ucred not defined */
264     struct ucred cred;
265     socklen_t cred_size = sizeof(cred);
266     struct passwd *p_entry = NULL;
267
268     setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true,
269                sizeof(true));
270     if (getsockopt
271         (client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, &cred,
272          &cred_size) != 0) {
273         error_e
274             ("Could not get client credentials using getsockopt(SO_PEERCRED)");
275         return;
276     }
277
278     p_entry = getpwuid(cred.uid);
279     if (p_entry == NULL) {
280         error_e("Could not find password entry for uid %d", cred.uid);
281         return;
282     }
283
284     /* Successfully identified user: */
285     client->fcl_user = strdup2(p_entry->pw_name);
286
287     explain("Client's pid=%d, uid=%d, gid=%d username=%s", cred.pid, cred.uid,
288             cred.gid, client->fcl_user);
289
290 }
291 #endif                          /* SO_PEERCRED */
292
293 void
294 auth_client_password(struct fcrondyn_cl *client)
295     /* check client identity by asking him to input his password */
296 {
297     char *pass_cry = NULL;
298     char *pass_sys = NULL;
299     char *pass_str = NULL;
300
301 #ifdef HAVE_GETSPNAM
302     struct spwd *pass_sp = NULL;
303     if ((pass_sp = getspnam((char *)client->fcl_cmd)) == NULL) {
304         error_e("could not getspnam %s", (char *)client->fcl_cmd);
305         send(client->fcl_sock_fd, "0", sizeof("0"), 0);
306         return;
307     }
308     pass_sys = pass_sp->sp_pwdp;
309 #else
310     struct passwd *pass = NULL;
311     errno = 0;
312     if ((pass = getpwnam((char *)client->fcl_cmd)) == NULL) {
313         error_e("could not getpwnam %s", (char *)client->fcl_cmd);
314         send(client->fcl_sock_fd, "0", sizeof("0"), 0);
315         return;
316     }
317     pass_sys = pass->pw_passwd;
318 #endif
319
320     /* */
321     debug("auth_client_password() : socket : %d", client->fcl_sock_fd);
322     /* */
323
324     /* we need to limit auth failures : otherwise fcron may be used to "read"
325      * shadow password !!! (or to crack it using a test-all-possible-password attack) */
326     if (auth_fail > 0 && auth_nofail_since + AUTH_WAIT <= now)
327         /* no auth time exceeded : set counter to 0 */
328         auth_fail = 0;
329     if (auth_fail >= MAX_AUTH_FAIL) {
330         error("Too many authentication failures : try to connect later.");
331         send(client->fcl_sock_fd, "0", sizeof("0"), 0);
332         auth_fail = auth_nofail_since = 0;
333         return;
334     }
335
336     /* the password is stored after the user name */
337     pass_str = &((char *)client->fcl_cmd)[strlen((char *)client->fcl_cmd) + 1];
338     if ((pass_cry = crypt(pass_str, pass_sys)) == NULL) {
339         error_e("could not crypt()");
340         send(client->fcl_sock_fd, "0", sizeof("0"), 0);
341         Overwrite(pass_str);
342         return;
343     }
344
345 /*      debug("pass_sp->sp_pwdp : %s", pass_sp->sp_pwdp); */
346 /*      debug("pass_cry : %s", pass_cry); */
347     if (strcmp(pass_cry, pass_sys) == 0) {
348         client->fcl_user = strdup2((char *)client->fcl_cmd);
349         send(client->fcl_sock_fd, "1", sizeof("1"), 0);
350     }
351     else {
352         auth_fail++;
353         auth_nofail_since = now;
354         error("Invalid passwd for %s from socket %d",
355               (char *)client->fcl_cmd, client->fcl_sock_fd);
356         send(client->fcl_sock_fd, "0", sizeof("0"), 0);
357     }
358
359     Overwrite(pass_str);
360 }
361
362
363 #define Test_add_field(FIELD_NB, FIELD_STR) \
364     if ( (bit_test(details, FIELD_NB)) ) { \
365         strncat(fields, FIELD_STR, sizeof(fields)-1 - len); \
366         len += (sizeof(FIELD_STR)-1); \
367     }
368 #define Add_field(FIELD_STR) \
369     strncat(fields, FIELD_STR, sizeof(fields) - len - 1); \
370     len += (sizeof(FIELD_STR)-1);
371
372 void
373 print_fields(int fd, unsigned char *details)
374     /* print a line describing the field types used in print_line() */
375 {
376     char fields[TERM_LEN];
377     char field_id[] = "ID   ";
378     char field_user[] = "|USER     ";
379     char field_rq[] = "|R&Q ";
380     char field_options[] = "|OPTIONS  ";
381     char field_schedule[] = "|SCHEDULE        ";
382     char field_until[] = "|LAVG 1,5,15 UNTIL       STRICT";
383     char field_pid[] = "|PID    ";
384     char field_index[] = "|INDEX";
385     char field_cmd[] = "|CMD";
386     char field_endline[] = "\n";
387     int len = 0;
388
389     fields[0] = '\0';
390
391     Add_field(field_id);
392     Test_add_field(FIELD_USER, field_user);
393     Test_add_field(FIELD_PID, field_pid);
394     Test_add_field(FIELD_INDEX, field_index);
395     Test_add_field(FIELD_RQ, field_rq);
396     Test_add_field(FIELD_OPTIONS, field_options);
397     Test_add_field(FIELD_LAVG, field_until);
398     Test_add_field(FIELD_SCHEDULE, field_schedule);
399     Add_field(field_cmd);
400     Add_field(field_endline);
401
402     fields[TERM_LEN - 1] = '\0';
403
404     if (send(fd, fields, (len < sizeof(fields)) ? len : sizeof(fields), 0) < 0)
405         error_e("error in send()");
406
407 }
408
409
410 void
411 print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid,
412            int index, time_t until)
413     /* print some basic fields of a line, and some more if details == 1 */
414 {
415     char buf[TERM_LEN];
416     int len = 0;
417     struct tm *ftime;
418
419
420     len = snprintf(buf, sizeof(buf), "%-5ld", line->cl_id);
421     if (bit_test(details, FIELD_USER))
422         len +=
423             snprintf(buf + len, sizeof(buf) - len, "|%-9s",
424                      line->cl_file->cf_user);
425     if (bit_test(details, FIELD_PID))
426         len += snprintf(buf + len, sizeof(buf) - len, "|%-7d", (int)pid);
427     if (bit_test(details, FIELD_INDEX))
428         len += snprintf(buf + len, sizeof(buf) - len, "|%-5d", index);
429     if (bit_test(details, FIELD_RQ))
430         len += snprintf(buf + len, sizeof(buf) - len, "|%-4d", line->cl_numexe);
431     if (bit_test(details, FIELD_OPTIONS)) {
432         char opt[9];
433         int i = 0;
434         opt[0] = '\0';
435         if (is_lavg(line->cl_option))
436             i += snprintf(opt + i, sizeof(opt) - i, "L%.*s",
437                           (is_lavg_sev(line->cl_option)) ? 0 : 1, "O");
438         if (is_serial(line->cl_option))
439             i += snprintf(opt + i, sizeof(opt) - i, "%.*sS%.*s", i, ",",
440                           (is_serial_sev(line->cl_option)) ? 0 : 1, "O");
441         if (is_exe_sev(line->cl_option))
442             i += snprintf(opt + i, sizeof(opt) - i, "%.*sES", i, ",");
443
444         len += snprintf(buf + len, sizeof(buf) - len, "|%-9s", opt);
445     }
446     if (bit_test(details, FIELD_LAVG)) {
447         len += snprintf(buf + len, sizeof(buf) - len, "|%.1f,%.1f,%.1f",
448                         ((double)((line->cl_lavg)[0])) / 10,
449                         ((double)((line->cl_lavg)[1])) / 10,
450                         ((double)((line->cl_lavg)[2])) / 10);
451         if (until > 0) {
452             ftime = localtime(&until);
453             len +=
454                 snprintf(buf + len, sizeof(buf) - len,
455                          " %04d-%02d-%02d %02d:%02d %s",
456                          (ftime->tm_year + 1900), (ftime->tm_mon + 1),
457                          ftime->tm_mday, ftime->tm_hour, ftime->tm_min,
458                          (is_strict(line->cl_option)) ? "Y" : "N");
459         }
460         else
461             len +=
462                 snprintf(buf + len, sizeof(buf) - len, " %18s",
463                          " (no until set) ");
464     }
465     if (bit_test(details, FIELD_SCHEDULE)) {
466         ftime = localtime(&(line->cl_nextexe));
467         len +=
468             snprintf(buf + len, sizeof(buf) - len, "|%04d-%02d-%02d %02d:%02d",
469                      (ftime->tm_year + 1900), (ftime->tm_mon + 1),
470                      ftime->tm_mday, ftime->tm_hour, ftime->tm_min);
471     }
472     len += snprintf(buf + len, sizeof(buf) - len, "|%s\n", line->cl_shell);
473
474     if (send(fd, buf, (len < sizeof(buf)) ? len : sizeof(buf), 0) < 0)
475         error_e("error in send()");
476
477 }
478
479
480 #define Test_line(LINE, PID, INDEX, UNTIL) \
481             { \
482                 if (all || strcmp(user, LINE->cl_file->cf_user) == 0 ) { \
483                     print_line(fd, LINE, fields, PID, INDEX, UNTIL); \
484                     found = 1; \
485                 } \
486             }
487 void
488 cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
489     /* run a command which lists some jobs */
490 {
491     int found = 0;
492     int all = (cmd[1] == ALL) ? 1 : 0;
493     char *user = NULL;
494     struct job_t *j;
495     int i;
496     unsigned char fields[FIELD_NUM_SIZE];
497     exe_t *e = NULL;
498     lavg_t *l = NULL;
499
500     for (i = 0; i < FIELD_NUM_SIZE; i++)
501         fields[i] = 0;
502
503     switch (cmd[0]) {
504     case CMD_DETAILS:
505         bit_set(fields, FIELD_SCHEDULE);
506         bit_set(fields, FIELD_RQ);
507         bit_set(fields, FIELD_USER);
508         bit_set(fields, FIELD_OPTIONS);
509         print_fields(fd, fields);
510         for (j = queue_base; j != NULL; j = j->j_next) {
511             if (cmd[1] == j->j_line->cl_id) {
512                 if (strcmp(client->fcl_user, j->j_line->cl_file->cf_user) == 0
513                     || is_root)
514                     print_line(fd, j->j_line, fields, 0, 0, 0);
515                 else
516                     Send_err_msg(fd, err_job_nfound_str);
517                 found = 1;
518                 break;
519             }
520         }
521         break;
522
523     case CMD_LIST_JOBS:
524     case CMD_LIST_LAVGQ:
525     case CMD_LIST_SERIALQ:
526     case CMD_LIST_EXEQ:
527         if (cmd[0] == CMD_LIST_LAVGQ) {
528             double lavg[3] = { 0, 0, 0 };
529             char lavg_str[TERM_LEN];
530             getloadavg(lavg, 3);
531             i = snprintf(lavg_str, sizeof(lavg_str), "Current load average : "
532                          "%.1f, %.1f, %.1f\n", lavg[0], lavg[1], lavg[2]);
533             send(fd, lavg_str, i, 0);
534
535             bit_set(fields, FIELD_LAVG);
536         }
537         else
538             bit_set(fields, FIELD_SCHEDULE);
539
540         if (cmd[0] == CMD_LIST_SERIALQ)
541             bit_set(fields, FIELD_INDEX);
542
543         if (cmd[0] == CMD_LIST_EXEQ)
544             bit_set(fields, FIELD_PID);
545
546         if (all && !is_root) {
547             warn("User %s tried to list *all* jobs.", client->fcl_user);
548             Send_err_msg_end(fd, err_all_nallowed_str);
549             return;
550         }
551         if (all)
552             bit_set(fields, FIELD_USER);
553         print_fields(fd, fields);
554
555         if (!all) {
556             struct passwd *pass;
557
558 #ifdef SYSFCRONTAB
559             if (cmd[1] == SYSFCRONTAB_UID)
560                 user = SYSFCRONTAB;
561             else {
562 #endif
563                 if ((pass = getpwuid((uid_t) cmd[1])) == NULL) {
564                     warn_e("Unable to find passwd entry for %ld", cmd[1]);
565                     Send_err_msg_end(fd, err_invalid_user_str);
566                     return;
567                 }
568                 if (!is_root && strcmp(pass->pw_name, client->fcl_user) != 0) {
569                     warn_e("%s is not allowed to see %s's jobs. %ld",
570                            client->fcl_user, pass->pw_name);
571                     Send_err_msg_end(fd, err_others_nallowed_str);
572                     return;
573                 }
574                 user = pass->pw_name;
575 #ifdef SYSFCRONTAB
576             }
577 #endif
578         }
579
580         /* list all jobs one by one and find the corresponding ones */
581         switch (cmd[0]) {
582         case CMD_LIST_JOBS:
583             for (j = queue_base; j != NULL; j = j->j_next)
584                 Test_line(j->j_line, 0, 0, 0);
585             break;
586
587         case CMD_LIST_EXEQ:
588             for (e = exe_list_first(exe_list); e != NULL;
589                  e = exe_list_next(exe_list)) {
590                 if (e->e_line == NULL) {
591                     if (is_root) {
592                         send_msg_fd(fd, "job no more in an fcrontab: pid %d",
593                                     e->e_job_pid);
594                         found = 1;
595                     }
596                 }
597                 else {
598                     Test_line(e->e_line, e->e_job_pid, 0, 0);
599                 }
600             }
601             break;
602
603         case CMD_LIST_LAVGQ:
604             for (l = lavg_list_first(lavg_list); l != NULL;
605                  l = lavg_list_next(lavg_list))
606                 Test_line(l->l_line, 0, 0, l->l_until);
607             break;
608
609         case CMD_LIST_SERIALQ:
610             {
611                 int j;
612                 i = serial_array_index;
613                 for (j = 0; j < serial_num; j++) {
614                     Test_line(serial_array[i], 0, j, 0);
615                     if (++i >= serial_array_size)
616                         i -= serial_array_size;
617                 }
618                 break;
619             }
620
621         }
622
623         break;
624     }
625
626     if (!found)
627         Send_err_msg(fd, err_job_nfound_str);
628     /* to tell fcrondyn there's no more data to wait */
629     Tell_no_more_data(fd);
630
631 }
632
633
634 void
635 cmd_on_exeq(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
636 /* common code to all cmds working on jobs in the exeq */
637 {
638     int found = 0;
639     char *err_str = NULL;
640     exe_t *e = NULL;
641
642     /* find the corresponding job */
643     for (e = exe_list_first(exe_list); e != NULL; e = exe_list_next(exe_list)) {
644         if (e->e_line != NULL && cmd[2] == e->e_line->cl_id) {
645
646             found = 1;
647
648             /* check if the request is valid */
649             if (!is_root &&
650                 strcmp(client->fcl_user, e->e_line->cl_file->cf_user) != 0) {
651
652                 if (cmd[0] == CMD_RENICE)
653                     err_str = "%s tried to renice to %ld job id %ld for %s : "
654                         "not allowed.";
655                 else if (cmd[0] == CMD_SEND_SIGNAL)
656                     err_str = "%s tried to send signal %ld to id %ld for %s : "
657                         "not allowed.";
658                 else
659                     err_str = "cannot run unknown cmd with arg %ld on job id "
660                         "%ld for %s : not allowed.";
661
662                 warn(err_str, client->fcl_user, cmd[1], cmd[2],
663                      client->fcl_user);
664                 Send_err_msg_end(fd, err_job_nfound_str);
665             }
666             else {
667                 /* request is valid : do it */
668
669                 if (cmd[0] == CMD_SEND_SIGNAL)
670                     cmd_send_signal(client, cmd, fd, e);
671                 else if (cmd[0] == CMD_RENICE)
672                     cmd_renice(client, cmd, fd, e, is_root);
673                 else {
674                     Send_err_msg_end(fd, err_cmd_unknown_str);
675                     exe_list_end_iteration(exe_list);
676                     return;
677                 }
678             }
679         }
680     }
681
682     if (!found) {
683
684         if (cmd[0] == CMD_RENICE)
685             err_str = "cannot renice job id %ld for %s : no corresponding "
686                 "running job.";
687         else if (cmd[0] == CMD_SEND_SIGNAL)
688             err_str = "cannot send signal to job id %ld for %s :"
689                 " no corresponding running job.";
690         else
691             err_str = "cannot run unknown cmd on job id %ld for %s :"
692                 " no corresponding running job.";
693
694         warn(err_str, cmd[2], client->fcl_user);
695         Send_err_msg_end(fd, err_rjob_nfound_str);
696     }
697     else {
698         Tell_no_more_data(fd);
699     }
700
701 }
702
703
704 void
705 cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
706            int is_root)
707 /* change nice value of a running job */
708 {
709
710 #ifdef HAVE_SETPRIORITY
711     /* check if arguments are valid */
712     if (e->e_job_pid <= 0 || ((int)cmd[1] < 0 && !is_root)
713         || (int)cmd[1] > 20 || (int)cmd[1] < -20) {
714         warn("renice: invalid args : pid: %d nice_value: %d user: %s.",
715              e->e_job_pid, (int)cmd[1], client->fcl_user);
716         Send_err_msg_end(fd, err_invalid_args_str);
717         return;
718     }
719
720     /* ok, now setpriority() the job */
721     if (setpriority(PRIO_PROCESS, e->e_job_pid, (int)cmd[1]) != 0) {
722         error_e("could not setpriority(PRIO_PROCESS, %d, %d)",
723                 e->e_job_pid, (int)cmd[1]);
724         Send_err_msg_end(fd, err_unknown_str);
725         return;
726     }
727     else {
728         send_msg_fd(fd, "Command successfully completed on process %d.",
729                     e->e_job_pid);
730         return;
731     }
732
733 #else                           /* HAVE_SETPRIORITY */
734     warn("System has no setpriority() : cannot renice. pid: %d nice_value: %d user: %s.", e->e_job_pid, (int)cmd[1], client->fcl_user);
735     Send_err_msg_end(fd, err_cmd_unknown_str);
736
737 #endif                          /* HAVE_SETPRIORITY */
738 }
739
740
741 void
742 cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e)
743 /* send a signal to a running job */
744 {
745     if (e->e_job_pid <= 0 || (int)cmd[1] <= 0) {
746         warn("send_signal: invalid args : pid: %d signal: %d user: %s",
747              e->e_job_pid, (int)cmd[1], client->fcl_user);
748         Send_err_msg_end(fd, err_invalid_args_str);
749         return;
750     }
751
752     /* ok, now kill() the job */
753     if (kill(e->e_job_pid, (int)cmd[1]) != 0) {
754         error_e("could not kill(%d, %d)", e->e_job_pid, (int)cmd[1]);
755         Send_err_msg_end(fd, err_unknown_str);
756         return;
757     }
758     else {
759         send_msg_fd(fd, "Command successfully completed on process %d.",
760                     e->e_job_pid);
761         return;
762     }
763 }
764
765
766 void
767 cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
768     /* Run a job and rescheduled if requested  */
769 {
770
771     struct job_t *j = NULL;
772
773     for (j = queue_base; j != NULL; j = j->j_next) {
774         if (cmd[1] == j->j_line->cl_id) {
775             if (strcmp(client->fcl_user, j->j_line->cl_file->cf_user) == 0
776                 || is_root) {
777
778                 if (is_lavg(j->j_line->cl_option))
779                     add_lavg_job(j->j_line, fd);
780                 else if (is_serial(j->j_line->cl_option))
781                     add_serial_job(j->j_line, fd);
782                 else
783                     run_normal_job(j->j_line, fd);
784
785                 if (cmd[0] == CMD_RUNNOW)
786                     set_next_exe(j->j_line, FROM_CUR_NEXTEXE, fd);
787
788                 Tell_no_more_data(fd);
789
790                 return;
791
792             }
793         }
794     }
795
796     /* we don't come here if a job has been found */
797     Send_err_msg_end(fd, err_job_nfound_str);
798
799 }
800
801 void
802 exe_cmd(struct fcrondyn_cl *client)
803     /* read command, and call corresponding function */
804 {
805     long int *cmd;
806     int fd;
807     int is_root = 0;
808
809     is_root = (strcmp(client->fcl_user, ROOTNAME) == 0) ? 1 : 0;
810
811     /* to make things clearer (avoid repeating client->fcl_ all the time) */
812     cmd = client->fcl_cmd;
813     fd = client->fcl_sock_fd;
814
815     /* */
816     debug("exe_cmd [0,1,2] : %d %d %d", cmd[0], cmd[1], cmd[2]);
817     /* */
818
819     switch (cmd[0]) {
820
821     case CMD_SEND_SIGNAL:
822     case CMD_RENICE:
823         cmd_on_exeq(client, cmd, fd, is_root);
824         break;
825
826     case CMD_DETAILS:
827     case CMD_LIST_JOBS:
828     case CMD_LIST_LAVGQ:
829     case CMD_LIST_SERIALQ:
830     case CMD_LIST_EXEQ:
831         cmd_ls(client, cmd, fd, is_root);
832         break;
833
834     case CMD_RUN:
835     case CMD_RUNNOW:
836         cmd_run(client, cmd, fd, is_root);
837         break;
838
839     default:
840         Send_err_msg_end(fd, err_cmd_unknown_str);
841     }
842 }
843
844 void
845 remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client,
846                   select_instance * si)
847 /* close the connection, remove it from the list 
848 and make client points to the next entry */
849 {
850     debug("closing connection : fd : %d", (*client)->fcl_sock_fd);
851     shutdown((*client)->fcl_sock_fd, SHUT_RDWR);
852     select_rm_read(si, (*client)->fcl_sock_fd);
853     xclose_check(&((*client)->fcl_sock_fd), "client fd");
854     if (prev_client == NULL) {
855         fcrondyn_cl_base = (*client)->fcl_next;
856         Free_safe((*client)->fcl_user);
857         Free_safe(*client);
858         *client = fcrondyn_cl_base;
859     }
860     else {
861         prev_client->fcl_next = (*client)->fcl_next;
862         Free_safe((*client)->fcl_user);
863         Free_safe(*client);
864         *client = prev_client->fcl_next;
865     }
866     fcrondyn_cl_num -= 1;
867 }
868
869 void
870 fcrondyn_socket_check(select_instance * si)
871     /* check for new connection, command, connection closed */
872 {
873     int fd = -1, avoid_fd = -1;
874     socklen_t addr_len = sizeof(struct sockaddr_un);
875     struct sockaddr_un client_addr;
876     long int buf_int[SOCKET_MSG_LEN];
877     int read_len = 0;
878     struct fcrondyn_cl *client = NULL, *prev_client = NULL;
879
880     if (si->retcode <= 0)
881         /* no socket to check : go directly to the end of that function */
882         return;
883
884     debug("Checking socket ...");
885
886     if (FD_ISSET(listen_fd, &si->readfds)) {
887         debug("got new connection ...");
888         fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
889         if (fd == -1) {
890             error_e
891                 ("could not accept new connection : isset(listen_fd = %d) = %d",
892                  listen_fd, FD_ISSET(listen_fd, &si->readfds));
893         }
894         else {
895             fcntl(fd, F_SETFD, 1);
896             /* set fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
897             if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
898                 error_e
899                     ("Could not set fd attribute O_NONBLOCK : connection rejected.");
900                 shutdown(fd, SHUT_RDWR);
901                 xclose_check(&fd, "client fd");
902             }
903             else {
904                 Alloc(client, fcrondyn_cl);
905                 client->fcl_sock_fd = fd;
906                 /* means : not authenticated yet : */
907                 client->fcl_user = NULL;
908                 client->fcl_cmd = NULL;
909
910                 /* include new entry in client list */
911                 client->fcl_next = fcrondyn_cl_base;
912                 fcrondyn_cl_base = client;
913                 client->fcl_idle_since = now;
914                 /* to avoid trying to read from it in this call */
915                 avoid_fd = fd;
916
917                 select_add_read(si, fd);
918                 fcrondyn_cl_num += 1;
919
920                 debug("Added connection fd : %d - %d connections", fd,
921                       fcrondyn_cl_num);
922
923 #ifdef SO_PEERCRED
924                 auth_client_so_peercred(client);
925 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)
926                 auth_client_getpeer(client);
927 #endif                          /* SO_PEERCRED */
928             }
929         }
930     }
931
932     client = fcrondyn_cl_base;
933     while (client != NULL) {
934         if (!FD_ISSET(client->fcl_sock_fd, &si->readfds)
935             || client->fcl_sock_fd == avoid_fd) {
936             /* check if the connection has not been idle for too long ... */
937             if (client->fcl_user == NULL
938                 && now - client->fcl_idle_since > MAX_AUTH_TIME) {
939                 warn("Connection with no auth for more than %ds : closing it.",
940                      MAX_AUTH_TIME);
941                 remove_connection(&client, prev_client, si);
942             }
943             else if (now - client->fcl_idle_since > MAX_IDLE_TIME) {
944                 warn("Connection of %s is idle for more than %ds : closing it.",
945                      client->fcl_user, MAX_IDLE_TIME);
946                 remove_connection(&client, prev_client, si);
947             }
948             else {
949                 /* nothing to do on this one ... check the next one */
950                 prev_client = client;
951                 client = client->fcl_next;
952             }
953             continue;
954         }
955
956         if ((read_len =
957              recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0) {
958             if (read_len == 0) {
959                 /* connection closed by client */
960                 remove_connection(&client, prev_client, si);
961             }
962             else {
963                 error_e("error recv() from sock fd %d", client->fcl_sock_fd);
964                 prev_client = client;
965                 client = client->fcl_next;
966             }
967         }
968         else {
969             client->fcl_cmd_len = read_len;
970             client->fcl_cmd = buf_int;
971             if (client->fcl_user == NULL)
972                 /* not authenticated yet */
973                 auth_client_password(client);
974             else {
975                 /* we've just read a command ... */
976                 client->fcl_idle_since = now;
977                 exe_cmd(client);
978             }
979             prev_client = client;
980             client = client->fcl_next;
981         }
982     }
983
984 }
985
986
987 void
988 fcrondyn_socket_close(select_instance * si)
989     /* close connections, close socket, remove socket file.
990      * If si is not NULL, then remove the fds from si's readfds */
991 {
992     struct fcrondyn_cl *client, *client_buf = NULL;
993
994     if (listen_fd) {
995         shutdown(listen_fd, SHUT_RDWR);
996         xclose_check(&listen_fd, "listening fd");
997         unlink(fifofile);
998
999         client = fcrondyn_cl_base;
1000         while (client != NULL) {
1001             shutdown(client->fcl_sock_fd, SHUT_RDWR);
1002             xclose_check(&(client->fcl_sock_fd), "client fd");
1003             if (si != NULL) {
1004                 select_rm_read(si, client->fcl_sock_fd);
1005             }
1006
1007             client_buf = client->fcl_next;
1008             Free_safe(client);
1009             fcrondyn_cl_num -= 1;
1010             client = client_buf;
1011         }
1012     }
1013 }