2 * FCRON - periodic command scheduler
4 * Copyright 2000-2012 Thibault Godouet <fcron@free.fr>
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.
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.
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
20 * The GNU General Public License can also be found in the file
21 * `LICENSE' that comes with the fcron source distribution.
25 /* This file contains all fcron's code (server) to handle communication with fcrondyn */
30 #include "getloadavg.h"
32 #include "fcronconf.h"
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);
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,
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,
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);
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 */
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";
77 /* Send an error message to fcrondyn */
78 #define Send_err_msg(FD, MSG) \
79 send(FD, MSG, sizeof(MSG), 0);
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);
85 /* Send an error message to fcrondyn and return */
86 #define Send_err_msg_end(FD, MSG) \
88 send(FD, MSG, sizeof(MSG), 0); \
89 Tell_no_more_data(FD); \
92 /* which bit corresponds to which field ? */
96 #define FIELD_SCHEDULE 3
99 #define FIELD_OPTIONS 6
101 /* the number of char we need (8 bits (i.e. fields) per char) */
102 #define FIELD_NUM_SIZE 1
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()) */
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()) */
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;
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;
131 *max_fd = tmp_max_fd;
138 /* do everything needed to get a working listening socket */
140 struct sockaddr_un addr;
144 /* used in fcron.c:main_loop():select() */
146 FD_ZERO(&master_set);
148 if ( (listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1 ) {
149 error_e("Could not create socket : fcrondyn won't work");
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);
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;
163 addr.sun_len = sun_len;
167 if (bind(listen_fd, (struct sockaddr*) &addr, sun_len) != 0){
168 error_e("Cannot bind socket to '%s'", fifofile);
172 if ( listen(listen_fd, MAX_CONNECTION) != 0 ) {
173 error_e("Cannot set socket in listen mode");
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");
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");
192 add_to_select_set(listen_fd, &master_set, &set_max_fd);
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);
204 #if defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)
207 * WARNING: UNTESTED CODE !!!
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 */
217 struct passwd *p_entry = NULL;
219 ucred_t *ucred = NULL;
220 #elif defined(HAVE_GETPEEREID)
226 if (getpeerucred(client->fcl_sock_fd, &ucred) < 0) {
227 error_e("Could not get client credentials using getpeerucred()");
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()");
236 # error "No authentication method in auth_client_getpeer()!"
239 p_entry = getpwuid(cred.uid);
240 if ( p_entry == NULL ) {
241 error_e("Could not find password entry for uid %d", cred.uid);
245 /* Successfully identified user: */
246 client->fcl_user = strdup2(p_entry->pw_name);
248 explain("Client's pid=%d, uid=%d, gid=%d username=%s\n", cred.pid, cred.uid, cred.gid, client->fcl_user);
251 #endif /* HAVE_GETPEERUCRED || HAVE_GETPEEREID */
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 */
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) )
273 #endif /* struct ucred not defined */
275 socklen_t cred_size = sizeof(cred);
276 struct passwd *p_entry = NULL;
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)");
285 p_entry = getpwuid(cred.uid);
286 if ( p_entry == NULL ) {
287 error_e("Could not find password entry for uid %d", cred.uid);
291 /* Successfully identified user: */
292 client->fcl_user = strdup2(p_entry->pw_name);
294 explain("Client's pid=%d, uid=%d, gid=%d username=%s\n", cred.pid, cred.uid, cred.gid, client->fcl_user);
297 #endif /* SO_PEERCRED */
300 auth_client_password(struct fcrondyn_cl *client)
301 /* check client identity by asking him to input his password */
303 char *pass_cry = NULL;
304 char *pass_sys = NULL;
305 char *pass_str = NULL;
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);
314 pass_sys = pass_sp->sp_pwdp;
316 struct passwd *pass = NULL;
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);
323 pass_sys = pass->pw_passwd;
327 debug("auth_client_password() : socket : %d", client->fcl_sock_fd);
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 */
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;
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);
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);
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);
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); \
374 #define Add_field(FIELD_STR) \
375 strncat(fields, FIELD_STR, sizeof(fields) - len - 1); \
376 len += (sizeof(FIELD_STR)-1);
379 print_fields(int fd, unsigned char *details)
380 /* print a line describing the field types used in print_line() */
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";
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);
408 fields[TERM_LEN-1] = '\0';
410 if ( send(fd, fields, (len < sizeof(fields)) ? len : sizeof(fields), 0) < 0 )
411 error_e("error in send()");
417 print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid, int index,
419 /* print some basic fields of a line, and some more if details == 1 */
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) ) {
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, ",");
448 len += snprintf(buf+len, sizeof(buf)-len, " %-9s", opt);
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);
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");
463 len += snprintf(buf+len, sizeof(buf)-len, " %18s", " (no until set) ");
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 );
471 len += snprintf(buf+len, sizeof(buf)-len, " %s\n", line->cl_shell);
473 if ( send(fd, buf, (len < sizeof(buf)) ? len : sizeof(buf), 0) < 0 )
474 error_e("error in send()");
479 #define Test_line(LINE, PID, INDEX, UNTIL) \
481 if (all || strcmp(user, LINE->cl_file->cf_user) == 0 ) { \
482 print_line(fd, LINE, fields, PID, INDEX, UNTIL); \
487 cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
488 /* run a command which lists some jobs */
491 int all = (cmd[1] == ALL) ? 1 : 0;
495 unsigned char fields[FIELD_NUM_SIZE];
499 for (i = 0; i < FIELD_NUM_SIZE; i++)
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
513 print_line(fd, j->j_line, fields, 0, 0, 0);
515 Send_err_msg(fd, err_job_nfound_str);
524 case CMD_LIST_SERIALQ:
526 if ( cmd[0] == CMD_LIST_LAVGQ ) {
527 double lavg[3] = {0, 0, 0};
528 char lavg_str[TERM_LEN];
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);
534 bit_set(fields, FIELD_LAVG);
537 bit_set(fields, FIELD_SCHEDULE);
539 if ( cmd[0] == CMD_LIST_SERIALQ )
540 bit_set(fields, FIELD_INDEX);
542 if ( cmd[0] == CMD_LIST_EXEQ )
543 bit_set(fields, FIELD_PID);
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);
551 bit_set(fields, FIELD_USER);
552 print_fields(fd, fields);
558 if ( cmd[1] == SYSFCRONTAB_UID )
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);
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,
570 Send_err_msg_end(fd, err_others_nallowed_str);
573 user = pass->pw_name;
579 /* list all jobs one by one and find the corresponding ones */
582 for ( j = queue_base; j != NULL; j = j->j_next )
583 Test_line(j->j_line, 0, 0, 0);
587 for (e = exe_list_first(exe_list); e != NULL; e = exe_list_next(exe_list)) {
588 if ( e->e_line == NULL ) {
590 send_msg_fd(fd, "job no more in an fcrontab: pid %d",
596 Test_line(e->e_line, e->e_job_pid, 0, 0);
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);
606 case CMD_LIST_SERIALQ:
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;
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);
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 */
636 char *err_str = NULL;
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 ) {
646 /* check if the request is valid */
648 strcmp(client->fcl_user, e->e_line->cl_file->cf_user) != 0 ) {
650 if ( cmd[0] == CMD_RENICE )
651 err_str = "%s tried to renice to %ld job id %ld for %s : "
653 else if (cmd[0] == CMD_SEND_SIGNAL)
654 err_str = "%s tried to send signal %ld to id %ld for %s : "
657 err_str = "cannot run unknown cmd with arg %ld on job id "
658 "%ld for %s : not allowed.";
660 warn(err_str, client->fcl_user, cmd[1], cmd[2],
662 Send_err_msg_end(fd, err_job_nfound_str);
665 /* request is valid : do it */
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);
672 Send_err_msg_end(fd, err_cmd_unknown_str);
673 exe_list_end_iteration(exe_list);
682 if ( cmd[0] == CMD_RENICE )
683 err_str = "cannot renice job id %ld for %s : no corresponding "
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.";
689 err_str = "cannot run unknown cmd on job id %ld for %s :"
690 " no corresponding running job.";
692 warn(err_str, cmd[2], client->fcl_user);
693 Send_err_msg_end(fd, err_rjob_nfound_str);
696 Tell_no_more_data(fd);
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 */
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);
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);
725 send_msg_fd(fd, "Command successfully completed on process %d.",
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);
735 #endif /* HAVE_SETPRIORITY */
740 cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t *e)
741 /* send a signal to a running job */
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);
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);
757 send_msg_fd(fd, "Command successfully completed on process %d.",
765 cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
766 /* Run a job and rescheduled if requested */
769 struct job_t *j = NULL;
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
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);
781 run_normal_job(j->j_line, fd);
783 if ( cmd[0] == CMD_RUNNOW )
784 set_next_exe(j->j_line, FROM_CUR_NEXTEXE, fd);
786 Tell_no_more_data(fd);
794 /* we don't come here if a job has been found */
795 Send_err_msg_end(fd, err_job_nfound_str);
800 exe_cmd(struct fcrondyn_cl *client)
801 /* read command, and call corresponding function */
807 is_root = (strcmp(client->fcl_user, ROOTNAME) == 0) ? 1 : 0;
809 /* to make things clearer (avoid repeating client->fcl_ all the time) */
810 cmd = client->fcl_cmd;
811 fd = client->fcl_sock_fd;
814 debug("exe_cmd [0,1,2] : %d %d %d", cmd[0], cmd[1], cmd[2]);
819 case CMD_SEND_SIGNAL:
821 cmd_on_exeq(client, cmd, fd, is_root);
827 case CMD_LIST_SERIALQ:
829 cmd_ls(client, cmd, fd, is_root);
834 cmd_run(client, cmd, fd, is_root);
838 Send_err_msg_end(fd, err_cmd_unknown_str);
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 */
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);
855 *client = fcrondyn_cl_base;
858 prev_client->fcl_next = (*client)->fcl_next;
859 Free_safe((*client)->fcl_user);
861 *client = prev_client->fcl_next;
863 fcrondyn_cl_num -= 1;
867 check_socket(int num)
868 /* check for new connection, command, connection closed */
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];
875 struct fcrondyn_cl *client = NULL, *prev_client = NULL;
878 /* no socket to check : go directly to the end of that function */
881 debug("Checking socket ...");
883 if ( FD_ISSET(listen_fd, &read_set) ) {
884 debug("got new connection ...");
885 fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
887 error_e("could not accept new connection : isset(listen_fd = %d) = %d",
888 listen_fd, FD_ISSET(listen_fd, &read_set));
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);
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;
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 */
912 add_to_select_set(fd, &master_set, &set_max_fd);
913 fcrondyn_cl_num += 1;
915 debug("Added connection fd : %d - %d connections", fd, fcrondyn_cl_num);
918 auth_client_so_peercred(client);
919 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)
920 auth_client_getpeer(client);
921 #endif /* SO_PEERCRED */
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.",
933 remove_connection(&client, prev_client);
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);
941 /* nothing to do on this one ... check the next one */
942 prev_client = client;
943 client = client->fcl_next;
948 if ( (read_len = recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0 ) {
950 /* connection closed by client */
951 remove_connection(&client, prev_client);
954 error_e("error recv() from sock fd %d", client->fcl_sock_fd);
955 prev_client = client;
956 client = client->fcl_next;
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);
966 /* we've just read a command ... */
967 client->fcl_idle_since = now;
970 prev_client = client;
971 client = client->fcl_next;
976 /* copy master_set in read_set, because read_set is modified by select() */
977 read_set = master_set;
983 /* close connections, close socket, remove socket file */
985 struct fcrondyn_cl *client, *client_buf = NULL;
988 shutdown(listen_fd, SHUT_RDWR);
992 client = fcrondyn_cl_base;
993 while ( client != NULL ) {
994 shutdown(client->fcl_sock_fd, SHUT_RDWR);
995 close(client->fcl_sock_fd);
997 client_buf = client->fcl_next;
999 fcrondyn_cl_num -= 1;
1000 client = client_buf;