2 * FCRON - periodic command scheduler
4 * Copyright 2000-2014 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 */
28 #include "fcrondyn_svr.h"
30 #include "getloadavg.h"
32 #include "fcronconf.h"
35 void remove_connection(struct fcrondyn_cl **client,
36 struct fcrondyn_cl *prev_client, select_instance * si);
37 void exe_cmd(struct fcrondyn_cl *client);
38 #ifdef SO_PEERCRED /* linux */
39 void auth_client_so_peercred(struct fcrondyn_cl *client);
40 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID) /* resp. solaris 10 and Free/OpenBSD) */
41 void auth_client_getpeer(struct fcrondyn_cl *client);
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,
50 void cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
52 void cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd,
54 void cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root);
56 fcrondyn_cl *fcrondyn_cl_base; /* list of connected fcrondyn clients */
57 int fcrondyn_cl_num = 0; /* number of fcrondyn clients currently connected */
58 int listen_fd = -1; /* fd which catches incoming connection */
59 int auth_fail = 0; /* number of auth failure */
60 time_t auth_nofail_since = 0; /* we refuse auth since x due to too many failures */
62 /* some error messages ... */
63 char err_no_err_str[] = "Command successfully completed.\n";
64 char err_unknown_str[] =
65 "Fcron has encountered an error : command not completed.\n";
66 char err_cmd_unknown_str[] = "Unknown command.\n";
67 char err_job_nfound_str[] = "No corresponding job found.\n";
68 char err_rjob_nfound_str[] =
69 "No corresponding running job found.\n (The job may have "
70 "just finished its execution.)\n";
71 char err_invalid_user_str[] = "Invalid user : unable to find a passwd entry.\n";
72 char err_invalid_args_str[] = "Invalid arguments.\n";
73 char err_job_nallowed_str[] = "You are not allowed to see/change this line.\n";
74 char err_all_nallowed_str[] = "You are not allowed to list all jobs.\n";
75 char err_others_nallowed_str[] =
76 "You are not allowed to list other users' jobs.\n";
78 /* Send an error message to fcrondyn */
79 #define Send_err_msg(FD, MSG) \
80 send(FD, MSG, sizeof(MSG), 0);
82 /* to tell fcrondyn there's no more data to wait */
83 #define Tell_no_more_data(FD) \
84 send(FD, END_STR, sizeof(END_STR), 0);
86 /* Send an error message to fcrondyn and return */
87 #define Send_err_msg_end(FD, MSG) \
89 send(FD, MSG, sizeof(MSG), 0); \
90 Tell_no_more_data(FD); \
93 /* which bit corresponds to which field ? */
97 #define FIELD_SCHEDULE 3
100 #define FIELD_OPTIONS 6
102 /* the number of char we need (8 bits (i.e. fields) per char) */
103 #define FIELD_NUM_SIZE 1
107 fcrondyn_socket_init(select_instance * si)
108 /* do everything needed to get a working listening socket */
110 struct sockaddr_un addr;
114 if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
115 error_e("Could not create socket : fcrondyn won't work");
119 addr.sun_family = AF_UNIX;
120 len = strlen(fifofile);
121 if (len > sizeof(addr.sun_path) - 1) {
122 error("Error : fifo file path too long (max is %d)",
123 sizeof(addr.sun_path) - 1);
126 strncpy(addr.sun_path, fifofile, sizeof(addr.sun_path));
127 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
128 sun_len = (addr.sun_path - (char *)&addr) + len;
130 addr.sun_len = sun_len;
134 if (bind(listen_fd, (struct sockaddr *)&addr, sun_len) != 0) {
135 error_e("Cannot bind socket to '%s'", fifofile);
139 if (listen(listen_fd, MAX_CONNECTION) != 0) {
140 error_e("Cannot set socket in listen mode");
145 /* The exec bit is not necessary and ignored on all systems but AIX, where it is
146 * needed to allow fcrondyn to open the file */
149 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH |
150 S_IWOTH | S_IXOTH) != 0)
151 error_e("Cannot chmod() socket file");
154 fcntl(listen_fd, F_SETFD, 1);
155 /* set listen_fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
156 if (fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL) | O_NONBLOCK) == -1) {
158 ("Could not set listen_fd attribute O_NONBLOCK : no fcrondyn support");
163 select_add_read(si, listen_fd);
165 debug("Socket initialized : listen_fd : %d", listen_fd);
169 fcrondyn_socket_close(si);
173 #if defined(HAVE_GETPEERUCRED)
175 auth_client_getpeer(struct fcrondyn_cl *client)
176 /* check client identity by reading its credentials from the socket
177 * using getpeerucred() (Solaris 10 onward).
178 * Sets client->fcl_user on success, don't do anything on failure
179 * so that the client stays unauthenticated */
181 struct passwd *p_entry = NULL;
182 ucred_t *ucred = NULL;
185 if (getpeerucred(client->fcl_sock_fd, &ucred) < 0) {
186 error_e("Could not get client credentials using getpeerucred()");
189 uid = ucred_getruid(ucred);
191 error_e("Could not get client uid from ucred_t");
194 p_entry = getpwuid(uid);
195 if (p_entry == NULL) {
196 error_e("Could not find password entry for uid %d", uid);
200 /* Successfully identified user: */
201 client->fcl_user = strdup2(p_entry->pw_name);
203 explain("Client's pid=%d, uid=%d, username=%s",
204 ucred_getpid(ucred), uid, client->fcl_user);
207 #endif /* HAVE_GETPEERUCRED */
209 #if defined(HAVE_GETPEEREID)
211 * WARNING: UNTESTED CODE !!!
214 auth_client_getpeer(struct fcrondyn_cl *client)
215 /* check client identity by reading its credentials from the socket
216 * using getpeerucred() (Solaris 10 onward) or getpeereid(open/freeBSD).
217 * Sets client->fcl_user on success, don't do anything on failure
218 * so that the client stays unauthenticated */
220 struct passwd *p_entry = NULL;
224 if (getpeereid(client->fcl_sock_fd, &euid, &egid) < 0) {
225 error_e("Could not get client credentials using getpeereid()");
228 p_entry = getpwuid(euid);
229 if (p_entry == NULL) {
230 error_e("Could not find password entry for uid %d", euid);
234 /* Successfully identified user: */
235 client->fcl_user = strdup2(p_entry->pw_name);
237 explain("Client's uid=%d, gid=%d username=%s", euid, egid,
241 #endif /* HAVE_GETPEEREID */
247 auth_client_so_peercred(struct fcrondyn_cl *client)
248 /* check client identity by reading its credentials from the socket
249 * using getsockopt(SO_PEERCRED) (Linux)
250 * Sets client->fcl_user on success, don't do anything on failure
251 * so that the client stays unauthenticated */
254 /* There is no ucred.h (or equivalent) on linux to define struct ucred (!!)
255 * so we do it here */
256 #if ! ( defined(HAVE_CRED_H) && defined(HAVE_UCRED_H) \
257 && defined(HAVE_SYS_CRED_H) && defined(HAVE_SYS_UCRED_H) )
263 #endif /* struct ucred not defined */
265 socklen_t cred_size = sizeof(cred);
266 struct passwd *p_entry = NULL;
268 setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true,
271 (client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, &cred,
274 ("Could not get client credentials using getsockopt(SO_PEERCRED)");
278 p_entry = getpwuid(cred.uid);
279 if (p_entry == NULL) {
280 error_e("Could not find password entry for uid %d", cred.uid);
284 /* Successfully identified user: */
285 client->fcl_user = strdup2(p_entry->pw_name);
287 explain("Client's pid=%d, uid=%d, gid=%d username=%s", cred.pid, cred.uid,
288 cred.gid, client->fcl_user);
291 #endif /* SO_PEERCRED */
294 auth_client_password(struct fcrondyn_cl *client)
295 /* check client identity by asking him to input his password */
297 char *pass_cry = NULL;
298 char *pass_sys = NULL;
299 char *pass_str = NULL;
302 struct spwd *pass_sp = NULL;
303 if ((pass_sp = getspnam((char *)client->fcl_cmd)) == NULL) {
304 error_e("could not getspnam %s", (char *)client->fcl_cmd);
305 send(client->fcl_sock_fd, "0", sizeof("0"), 0);
308 pass_sys = pass_sp->sp_pwdp;
310 struct passwd *pass = NULL;
312 if ((pass = getpwnam((char *)client->fcl_cmd)) == NULL) {
313 error_e("could not getpwnam %s", (char *)client->fcl_cmd);
314 send(client->fcl_sock_fd, "0", sizeof("0"), 0);
317 pass_sys = pass->pw_passwd;
321 debug("auth_client_password() : socket : %d", client->fcl_sock_fd);
324 /* we need to limit auth failures : otherwise fcron may be used to "read"
325 * shadow password !!! (or to crack it using a test-all-possible-password attack) */
326 if (auth_fail > 0 && auth_nofail_since + AUTH_WAIT <= now)
327 /* no auth time exceeded : set counter to 0 */
329 if (auth_fail >= MAX_AUTH_FAIL) {
330 error("Too many authentication failures : try to connect later.");
331 send(client->fcl_sock_fd, "0", sizeof("0"), 0);
332 auth_fail = auth_nofail_since = 0;
336 /* the password is stored after the user name */
337 pass_str = &((char *)client->fcl_cmd)[strlen((char *)client->fcl_cmd) + 1];
338 if ((pass_cry = crypt(pass_str, pass_sys)) == NULL) {
339 error_e("could not crypt()");
340 send(client->fcl_sock_fd, "0", sizeof("0"), 0);
345 /* debug("pass_sp->sp_pwdp : %s", pass_sp->sp_pwdp); */
346 /* debug("pass_cry : %s", pass_cry); */
347 if (strcmp(pass_cry, pass_sys) == 0) {
348 client->fcl_user = strdup2((char *)client->fcl_cmd);
349 send(client->fcl_sock_fd, "1", sizeof("1"), 0);
353 auth_nofail_since = now;
354 error("Invalid passwd for %s from socket %d",
355 (char *)client->fcl_cmd, client->fcl_sock_fd);
356 send(client->fcl_sock_fd, "0", sizeof("0"), 0);
363 #define Test_add_field(FIELD_NB, FIELD_STR) \
364 if ( (bit_test(details, FIELD_NB)) ) { \
365 strncat(fields, FIELD_STR, sizeof(fields)-1 - len); \
366 len += (sizeof(FIELD_STR)-1); \
368 #define Add_field(FIELD_STR) \
369 strncat(fields, FIELD_STR, sizeof(fields) - len - 1); \
370 len += (sizeof(FIELD_STR)-1);
373 print_fields(int fd, unsigned char *details)
374 /* print a line describing the field types used in print_line() */
376 char fields[TERM_LEN];
377 char field_id[] = "ID ";
378 char field_user[] = "|USER ";
379 char field_rq[] = "|R&Q ";
380 char field_options[] = "|OPTIONS ";
381 char field_schedule[] = "|SCHEDULE ";
382 char field_until[] = "|LAVG 1,5,15 UNTIL STRICT";
383 char field_pid[] = "|PID ";
384 char field_index[] = "|INDEX";
385 char field_cmd[] = "|CMD";
386 char field_endline[] = "\n";
392 Test_add_field(FIELD_USER, field_user);
393 Test_add_field(FIELD_PID, field_pid);
394 Test_add_field(FIELD_INDEX, field_index);
395 Test_add_field(FIELD_RQ, field_rq);
396 Test_add_field(FIELD_OPTIONS, field_options);
397 Test_add_field(FIELD_LAVG, field_until);
398 Test_add_field(FIELD_SCHEDULE, field_schedule);
399 Add_field(field_cmd);
400 Add_field(field_endline);
402 fields[TERM_LEN - 1] = '\0';
404 if (send(fd, fields, (len < sizeof(fields)) ? len : sizeof(fields), 0) < 0)
405 error_e("error in send()");
411 print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid,
412 int index, time_t until)
413 /* print some basic fields of a line, and some more if details == 1 */
420 len = snprintf(buf, sizeof(buf), "%-5ld", line->cl_id);
421 if (bit_test(details, FIELD_USER))
423 snprintf(buf + len, sizeof(buf) - len, "|%-9s",
424 line->cl_file->cf_user);
425 if (bit_test(details, FIELD_PID))
426 len += snprintf(buf + len, sizeof(buf) - len, "|%-7d", (int)pid);
427 if (bit_test(details, FIELD_INDEX))
428 len += snprintf(buf + len, sizeof(buf) - len, "|%-5d", index);
429 if (bit_test(details, FIELD_RQ))
430 len += snprintf(buf + len, sizeof(buf) - len, "|%-4d", line->cl_numexe);
431 if (bit_test(details, FIELD_OPTIONS)) {
435 if (is_lavg(line->cl_option))
436 i += snprintf(opt + i, sizeof(opt) - i, "L%.*s",
437 (is_lavg_sev(line->cl_option)) ? 0 : 1, "O");
438 if (is_serial(line->cl_option))
439 i += snprintf(opt + i, sizeof(opt) - i, "%.*sS%.*s", i, ",",
440 (is_serial_sev(line->cl_option)) ? 0 : 1, "O");
441 if (is_exe_sev(line->cl_option))
442 i += snprintf(opt + i, sizeof(opt) - i, "%.*sES", i, ",");
444 len += snprintf(buf + len, sizeof(buf) - len, "|%-9s", opt);
446 if (bit_test(details, FIELD_LAVG)) {
447 len += snprintf(buf + len, sizeof(buf) - len, "|%.1f,%.1f,%.1f",
448 ((double)((line->cl_lavg)[0])) / 10,
449 ((double)((line->cl_lavg)[1])) / 10,
450 ((double)((line->cl_lavg)[2])) / 10);
452 ftime = localtime(&until);
454 snprintf(buf + len, sizeof(buf) - len,
455 " %04d-%02d-%02d %02d:%02d %s",
456 (ftime->tm_year + 1900), (ftime->tm_mon + 1),
457 ftime->tm_mday, ftime->tm_hour, ftime->tm_min,
458 (is_strict(line->cl_option)) ? "Y" : "N");
462 snprintf(buf + len, sizeof(buf) - len, " %18s",
465 if (bit_test(details, FIELD_SCHEDULE)) {
466 ftime = localtime(&(line->cl_nextexe));
468 snprintf(buf + len, sizeof(buf) - len, "|%04d-%02d-%02d %02d:%02d",
469 (ftime->tm_year + 1900), (ftime->tm_mon + 1),
470 ftime->tm_mday, ftime->tm_hour, ftime->tm_min);
472 len += snprintf(buf + len, sizeof(buf) - len, "|%s\n", line->cl_shell);
474 if (send(fd, buf, (len < sizeof(buf)) ? len : sizeof(buf), 0) < 0)
475 error_e("error in send()");
480 #define Test_line(LINE, PID, INDEX, UNTIL) \
482 if (all || strcmp(user, LINE->cl_file->cf_user) == 0 ) { \
483 print_line(fd, LINE, fields, PID, INDEX, UNTIL); \
488 cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
489 /* run a command which lists some jobs */
492 int all = (cmd[1] == ALL) ? 1 : 0;
496 unsigned char fields[FIELD_NUM_SIZE];
500 for (i = 0; i < FIELD_NUM_SIZE; i++)
505 bit_set(fields, FIELD_SCHEDULE);
506 bit_set(fields, FIELD_RQ);
507 bit_set(fields, FIELD_USER);
508 bit_set(fields, FIELD_OPTIONS);
509 print_fields(fd, fields);
510 for (j = queue_base; j != NULL; j = j->j_next) {
511 if (cmd[1] == j->j_line->cl_id) {
512 if (strcmp(client->fcl_user, j->j_line->cl_file->cf_user) == 0
514 print_line(fd, j->j_line, fields, 0, 0, 0);
516 Send_err_msg(fd, err_job_nfound_str);
525 case CMD_LIST_SERIALQ:
527 if (cmd[0] == CMD_LIST_LAVGQ) {
528 double lavg[3] = { 0, 0, 0 };
529 char lavg_str[TERM_LEN];
531 i = snprintf(lavg_str, sizeof(lavg_str), "Current load average : "
532 "%.1f, %.1f, %.1f\n", lavg[0], lavg[1], lavg[2]);
533 send(fd, lavg_str, i, 0);
535 bit_set(fields, FIELD_LAVG);
538 bit_set(fields, FIELD_SCHEDULE);
540 if (cmd[0] == CMD_LIST_SERIALQ)
541 bit_set(fields, FIELD_INDEX);
543 if (cmd[0] == CMD_LIST_EXEQ)
544 bit_set(fields, FIELD_PID);
546 if (all && !is_root) {
547 warn("User %s tried to list *all* jobs.", client->fcl_user);
548 Send_err_msg_end(fd, err_all_nallowed_str);
552 bit_set(fields, FIELD_USER);
553 print_fields(fd, fields);
559 if (cmd[1] == SYSFCRONTAB_UID)
563 if ((pass = getpwuid((uid_t) cmd[1])) == NULL) {
564 warn_e("Unable to find passwd entry for %ld", cmd[1]);
565 Send_err_msg_end(fd, err_invalid_user_str);
568 if (!is_root && strcmp(pass->pw_name, client->fcl_user) != 0) {
569 warn_e("%s is not allowed to see %s's jobs. %ld",
570 client->fcl_user, pass->pw_name);
571 Send_err_msg_end(fd, err_others_nallowed_str);
574 user = pass->pw_name;
580 /* list all jobs one by one and find the corresponding ones */
583 for (j = queue_base; j != NULL; j = j->j_next)
584 Test_line(j->j_line, 0, 0, 0);
588 for (e = exe_list_first(exe_list); e != NULL;
589 e = exe_list_next(exe_list)) {
590 if (e->e_line == NULL) {
592 send_msg_fd(fd, "job no more in an fcrontab: pid %d",
598 Test_line(e->e_line, e->e_job_pid, 0, 0);
604 for (l = lavg_list_first(lavg_list); l != NULL;
605 l = lavg_list_next(lavg_list))
606 Test_line(l->l_line, 0, 0, l->l_until);
609 case CMD_LIST_SERIALQ:
612 i = serial_array_index;
613 for (j = 0; j < serial_num; j++) {
614 Test_line(serial_array[i], 0, j, 0);
615 if (++i >= serial_array_size)
616 i -= serial_array_size;
627 Send_err_msg(fd, err_job_nfound_str);
628 /* to tell fcrondyn there's no more data to wait */
629 Tell_no_more_data(fd);
635 cmd_on_exeq(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
636 /* common code to all cmds working on jobs in the exeq */
639 char *err_str = NULL;
642 /* find the corresponding job */
643 for (e = exe_list_first(exe_list); e != NULL; e = exe_list_next(exe_list)) {
644 if (e->e_line != NULL && cmd[2] == e->e_line->cl_id) {
648 /* check if the request is valid */
650 strcmp(client->fcl_user, e->e_line->cl_file->cf_user) != 0) {
652 if (cmd[0] == CMD_RENICE)
653 err_str = "%s tried to renice to %ld job id %ld for %s : "
655 else if (cmd[0] == CMD_SEND_SIGNAL)
656 err_str = "%s tried to send signal %ld to id %ld for %s : "
659 err_str = "cannot run unknown cmd with arg %ld on job id "
660 "%ld for %s : not allowed.";
662 warn(err_str, client->fcl_user, cmd[1], cmd[2],
664 Send_err_msg_end(fd, err_job_nfound_str);
667 /* request is valid : do it */
669 if (cmd[0] == CMD_SEND_SIGNAL)
670 cmd_send_signal(client, cmd, fd, e);
671 else if (cmd[0] == CMD_RENICE)
672 cmd_renice(client, cmd, fd, e, is_root);
674 Send_err_msg_end(fd, err_cmd_unknown_str);
675 exe_list_end_iteration(exe_list);
684 if (cmd[0] == CMD_RENICE)
685 err_str = "cannot renice job id %ld for %s : no corresponding "
687 else if (cmd[0] == CMD_SEND_SIGNAL)
688 err_str = "cannot send signal to job id %ld for %s :"
689 " no corresponding running job.";
691 err_str = "cannot run unknown cmd on job id %ld for %s :"
692 " no corresponding running job.";
694 warn(err_str, cmd[2], client->fcl_user);
695 Send_err_msg_end(fd, err_rjob_nfound_str);
698 Tell_no_more_data(fd);
705 cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
707 /* change nice value of a running job */
710 #ifdef HAVE_SETPRIORITY
711 /* check if arguments are valid */
712 if (e->e_job_pid <= 0 || ((int)cmd[1] < 0 && !is_root)
713 || (int)cmd[1] > 20 || (int)cmd[1] < -20) {
714 warn("renice: invalid args : pid: %d nice_value: %d user: %s.",
715 e->e_job_pid, (int)cmd[1], client->fcl_user);
716 Send_err_msg_end(fd, err_invalid_args_str);
720 /* ok, now setpriority() the job */
721 if (setpriority(PRIO_PROCESS, e->e_job_pid, (int)cmd[1]) != 0) {
722 error_e("could not setpriority(PRIO_PROCESS, %d, %d)",
723 e->e_job_pid, (int)cmd[1]);
724 Send_err_msg_end(fd, err_unknown_str);
728 send_msg_fd(fd, "Command successfully completed on process %d.",
733 #else /* HAVE_SETPRIORITY */
734 warn("System has no setpriority() : cannot renice. pid: %d nice_value: %d user: %s.", e->e_job_pid, (int)cmd[1], client->fcl_user);
735 Send_err_msg_end(fd, err_cmd_unknown_str);
737 #endif /* HAVE_SETPRIORITY */
742 cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e)
743 /* send a signal to a running job */
745 if (e->e_job_pid <= 0 || (int)cmd[1] <= 0) {
746 warn("send_signal: invalid args : pid: %d signal: %d user: %s",
747 e->e_job_pid, (int)cmd[1], client->fcl_user);
748 Send_err_msg_end(fd, err_invalid_args_str);
752 /* ok, now kill() the job */
753 if (kill(e->e_job_pid, (int)cmd[1]) != 0) {
754 error_e("could not kill(%d, %d)", e->e_job_pid, (int)cmd[1]);
755 Send_err_msg_end(fd, err_unknown_str);
759 send_msg_fd(fd, "Command successfully completed on process %d.",
767 cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
768 /* Run a job and rescheduled if requested */
771 struct job_t *j = NULL;
773 for (j = queue_base; j != NULL; j = j->j_next) {
774 if (cmd[1] == j->j_line->cl_id) {
775 if (strcmp(client->fcl_user, j->j_line->cl_file->cf_user) == 0
778 if (is_lavg(j->j_line->cl_option))
779 add_lavg_job(j->j_line, fd);
780 else if (is_serial(j->j_line->cl_option))
781 add_serial_job(j->j_line, fd);
783 run_normal_job(j->j_line, fd);
785 if (cmd[0] == CMD_RUNNOW)
786 set_next_exe(j->j_line, FROM_CUR_NEXTEXE, fd);
788 Tell_no_more_data(fd);
796 /* we don't come here if a job has been found */
797 Send_err_msg_end(fd, err_job_nfound_str);
802 exe_cmd(struct fcrondyn_cl *client)
803 /* read command, and call corresponding function */
809 is_root = (strcmp(client->fcl_user, ROOTNAME) == 0) ? 1 : 0;
811 /* to make things clearer (avoid repeating client->fcl_ all the time) */
812 cmd = client->fcl_cmd;
813 fd = client->fcl_sock_fd;
816 debug("exe_cmd [0,1,2] : %d %d %d", cmd[0], cmd[1], cmd[2]);
821 case CMD_SEND_SIGNAL:
823 cmd_on_exeq(client, cmd, fd, is_root);
829 case CMD_LIST_SERIALQ:
831 cmd_ls(client, cmd, fd, is_root);
836 cmd_run(client, cmd, fd, is_root);
840 Send_err_msg_end(fd, err_cmd_unknown_str);
845 remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client,
846 select_instance * si)
847 /* close the connection, remove it from the list
848 and make client points to the next entry */
850 debug("closing connection : fd : %d", (*client)->fcl_sock_fd);
851 shutdown((*client)->fcl_sock_fd, SHUT_RDWR);
852 select_rm_read(si, (*client)->fcl_sock_fd);
853 xclose_check(&((*client)->fcl_sock_fd), "client fd");
854 if (prev_client == NULL) {
855 fcrondyn_cl_base = (*client)->fcl_next;
856 Free_safe((*client)->fcl_user);
858 *client = fcrondyn_cl_base;
861 prev_client->fcl_next = (*client)->fcl_next;
862 Free_safe((*client)->fcl_user);
864 *client = prev_client->fcl_next;
866 fcrondyn_cl_num -= 1;
870 fcrondyn_socket_check(select_instance * si)
871 /* check for new connection, command, connection closed */
873 int fd = -1, avoid_fd = -1;
874 socklen_t addr_len = sizeof(struct sockaddr_un);
875 struct sockaddr_un client_addr;
876 long int buf_int[SOCKET_MSG_LEN];
878 struct fcrondyn_cl *client = NULL, *prev_client = NULL;
880 if (si->retcode <= 0)
881 /* no socket to check : go directly to the end of that function */
884 debug("Checking socket ...");
886 if (FD_ISSET(listen_fd, &si->readfds)) {
887 debug("got new connection ...");
888 fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
891 ("could not accept new connection : isset(listen_fd = %d) = %d",
892 listen_fd, FD_ISSET(listen_fd, &si->readfds));
895 fcntl(fd, F_SETFD, 1);
896 /* set fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
897 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
899 ("Could not set fd attribute O_NONBLOCK : connection rejected.");
900 shutdown(fd, SHUT_RDWR);
901 xclose_check(&fd, "client fd");
904 Alloc(client, fcrondyn_cl);
905 client->fcl_sock_fd = fd;
906 /* means : not authenticated yet : */
907 client->fcl_user = NULL;
908 client->fcl_cmd = NULL;
910 /* include new entry in client list */
911 client->fcl_next = fcrondyn_cl_base;
912 fcrondyn_cl_base = client;
913 client->fcl_idle_since = now;
914 /* to avoid trying to read from it in this call */
917 select_add_read(si, fd);
918 fcrondyn_cl_num += 1;
920 debug("Added connection fd : %d - %d connections", fd,
924 auth_client_so_peercred(client);
925 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)
926 auth_client_getpeer(client);
927 #endif /* SO_PEERCRED */
932 client = fcrondyn_cl_base;
933 while (client != NULL) {
934 if (!FD_ISSET(client->fcl_sock_fd, &si->readfds)
935 || client->fcl_sock_fd == avoid_fd) {
936 /* check if the connection has not been idle for too long ... */
937 if (client->fcl_user == NULL
938 && now - client->fcl_idle_since > MAX_AUTH_TIME) {
939 warn("Connection with no auth for more than %ds : closing it.",
941 remove_connection(&client, prev_client, si);
943 else if (now - client->fcl_idle_since > MAX_IDLE_TIME) {
944 warn("Connection of %s is idle for more than %ds : closing it.",
945 client->fcl_user, MAX_IDLE_TIME);
946 remove_connection(&client, prev_client, si);
949 /* nothing to do on this one ... check the next one */
950 prev_client = client;
951 client = client->fcl_next;
957 recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0) {
959 /* connection closed by client */
960 remove_connection(&client, prev_client, si);
963 error_e("error recv() from sock fd %d", client->fcl_sock_fd);
964 prev_client = client;
965 client = client->fcl_next;
969 client->fcl_cmd_len = read_len;
970 client->fcl_cmd = buf_int;
971 if (client->fcl_user == NULL)
972 /* not authenticated yet */
973 auth_client_password(client);
975 /* we've just read a command ... */
976 client->fcl_idle_since = now;
979 prev_client = client;
980 client = client->fcl_next;
988 fcrondyn_socket_close(select_instance * si)
989 /* close connections, close socket, remove socket file.
990 * If si is not NULL, then remove the fds from si's readfds */
992 struct fcrondyn_cl *client, *client_buf = NULL;
995 shutdown(listen_fd, SHUT_RDWR);
996 xclose_check(&listen_fd, "listening fd");
999 client = fcrondyn_cl_base;
1000 while (client != NULL) {
1001 shutdown(client->fcl_sock_fd, SHUT_RDWR);
1002 xclose_check(&(client->fcl_sock_fd), "client fd");
1004 select_rm_read(si, client->fcl_sock_fd);
1007 client_buf = client->fcl_next;
1009 fcrondyn_cl_num -= 1;
1010 client = client_buf;