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