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