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 */
30 #include "getloadavg.h"
32 #include "fcronconf.h"
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);
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);
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);
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 */
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";
83 /* Send an error message to fcrondyn */
84 #define Send_err_msg(FD, MSG) \
85 send(FD, MSG, sizeof(MSG), 0);
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);
91 /* Send an error message to fcrondyn and return */
92 #define Send_err_msg_end(FD, MSG) \
94 send(FD, MSG, sizeof(MSG), 0); \
95 Tell_no_more_data(FD); \
98 /* which bit corresponds to which field ? */
102 #define FIELD_SCHEDULE 3
104 #define FIELD_INDEX 5
105 #define FIELD_OPTIONS 6
107 /* the number of char we need (8 bits (i.e. fields) per char) */
108 #define FIELD_NUM_SIZE 1
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()) */
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()) */
127 /* find the biggest fd in order to update max_fd */
128 struct fcrondyn_cl *client;
129 int tmp_max_fd = listen_fd;
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;
138 *max_fd = tmp_max_fd;
145 /* do everything needed to get a working listening socket */
147 struct sockaddr_un addr;
151 /* used in fcron.c:main_loop():select() */
153 FD_ZERO(&master_set);
155 if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
156 error_e("Could not create socket : fcrondyn won't work");
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);
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;
171 addr.sun_len = sun_len;
175 if (bind(listen_fd, (struct sockaddr *)&addr, sun_len) != 0) {
176 error_e("Cannot bind socket to '%s'", fifofile);
180 if (listen(listen_fd, MAX_CONNECTION) != 0) {
181 error_e("Cannot set socket in listen mode");
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 */
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");
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) {
199 ("Could not set listen_fd attribute O_NONBLOCK : no fcrondyn support");
204 add_to_select_set(listen_fd, &master_set, &set_max_fd);
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,
217 #if defined(HAVE_GETPEERUCRED)
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 */
225 struct passwd *p_entry = NULL;
226 ucred_t *ucred = NULL;
229 if (getpeerucred(client->fcl_sock_fd, &ucred) < 0) {
230 error_e("Could not get client credentials using getpeerucred()");
233 uid = ucred_getruid(ucred);
235 error_e("Could not get client uid from ucred_t");
238 p_entry = getpwuid(uid);
239 if (p_entry == NULL) {
240 error_e("Could not find password entry for uid %d", uid);
244 /* Successfully identified user: */
245 client->fcl_user = strdup2(p_entry->pw_name);
247 explain("Client's pid=%d, uid=%d, username=%s",
248 ucred_getpid(ucred), uid, client->fcl_user);
251 #endif /* HAVE_GETPEERUCRED */
253 #if defined(HAVE_GETPEEREID)
255 * WARNING: UNTESTED CODE !!!
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 */
264 struct passwd *p_entry = NULL;
268 if (getpeereid(client->fcl_sock_fd, &euid, &egid) < 0) {
269 error_e("Could not get client credentials using getpeereid()");
272 p_entry = getpwuid(euid);
273 if (p_entry == NULL) {
274 error_e("Could not find password entry for uid %d", euid);
278 /* Successfully identified user: */
279 client->fcl_user = strdup2(p_entry->pw_name);
281 explain("Client's uid=%d, gid=%d username=%s", euid, egid,
285 #endif /* HAVE_GETPEEREID */
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 */
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) )
307 #endif /* struct ucred not defined */
309 socklen_t cred_size = sizeof(cred);
310 struct passwd *p_entry = NULL;
312 setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true,
315 (client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, &cred,
318 ("Could not get client credentials using getsockopt(SO_PEERCRED)");
322 p_entry = getpwuid(cred.uid);
323 if (p_entry == NULL) {
324 error_e("Could not find password entry for uid %d", cred.uid);
328 /* Successfully identified user: */
329 client->fcl_user = strdup2(p_entry->pw_name);
331 explain("Client's pid=%d, uid=%d, gid=%d username=%s", cred.pid, cred.uid,
332 cred.gid, client->fcl_user);
335 #endif /* SO_PEERCRED */
338 auth_client_password(struct fcrondyn_cl *client)
339 /* check client identity by asking him to input his password */
341 char *pass_cry = NULL;
342 char *pass_sys = NULL;
343 char *pass_str = NULL;
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);
352 pass_sys = pass_sp->sp_pwdp;
354 struct passwd *pass = NULL;
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);
361 pass_sys = pass->pw_passwd;
365 debug("auth_client_password() : socket : %d", client->fcl_sock_fd);
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 */
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;
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);
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);
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);
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); \
412 #define Add_field(FIELD_STR) \
413 strncat(fields, FIELD_STR, sizeof(fields) - len - 1); \
414 len += (sizeof(FIELD_STR)-1);
417 print_fields(int fd, unsigned char *details)
418 /* print a line describing the field types used in print_line() */
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";
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);
446 fields[TERM_LEN - 1] = '\0';
448 if (send(fd, fields, (len < sizeof(fields)) ? len : sizeof(fields), 0) < 0)
449 error_e("error in send()");
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 */
464 len = snprintf(buf, sizeof(buf), "%-5ld", line->cl_id);
465 if (bit_test(details, FIELD_USER))
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)) {
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, ",");
488 len += snprintf(buf + len, sizeof(buf) - len, "|%-9s", opt);
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);
496 ftime = localtime(&until);
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");
506 snprintf(buf + len, sizeof(buf) - len, " %18s",
509 if (bit_test(details, FIELD_SCHEDULE)) {
510 ftime = localtime(&(line->cl_nextexe));
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);
516 len += snprintf(buf + len, sizeof(buf) - len, "|%s\n", line->cl_shell);
518 if (send(fd, buf, (len < sizeof(buf)) ? len : sizeof(buf), 0) < 0)
519 error_e("error in send()");
524 #define Test_line(LINE, PID, INDEX, UNTIL) \
526 if (all || strcmp(user, LINE->cl_file->cf_user) == 0 ) { \
527 print_line(fd, LINE, fields, PID, INDEX, UNTIL); \
532 cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
533 /* run a command which lists some jobs */
536 int all = (cmd[1] == ALL) ? 1 : 0;
540 unsigned char fields[FIELD_NUM_SIZE];
544 for (i = 0; i < FIELD_NUM_SIZE; i++)
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
558 print_line(fd, j->j_line, fields, 0, 0, 0);
560 Send_err_msg(fd, err_job_nfound_str);
569 case CMD_LIST_SERIALQ:
571 if (cmd[0] == CMD_LIST_LAVGQ) {
572 double lavg[3] = { 0, 0, 0 };
573 char lavg_str[TERM_LEN];
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);
579 bit_set(fields, FIELD_LAVG);
582 bit_set(fields, FIELD_SCHEDULE);
584 if (cmd[0] == CMD_LIST_SERIALQ)
585 bit_set(fields, FIELD_INDEX);
587 if (cmd[0] == CMD_LIST_EXEQ)
588 bit_set(fields, FIELD_PID);
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);
596 bit_set(fields, FIELD_USER);
597 print_fields(fd, fields);
603 if (cmd[1] == SYSFCRONTAB_UID)
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);
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);
618 user = pass->pw_name;
624 /* list all jobs one by one and find the corresponding ones */
627 for (j = queue_base; j != NULL; j = j->j_next)
628 Test_line(j->j_line, 0, 0, 0);
632 for (e = exe_list_first(exe_list); e != NULL;
633 e = exe_list_next(exe_list)) {
634 if (e->e_line == NULL) {
636 send_msg_fd(fd, "job no more in an fcrontab: pid %d",
642 Test_line(e->e_line, e->e_job_pid, 0, 0);
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);
653 case CMD_LIST_SERIALQ:
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;
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);
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 */
683 char *err_str = NULL;
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) {
692 /* check if the request is valid */
694 strcmp(client->fcl_user, e->e_line->cl_file->cf_user) != 0) {
696 if (cmd[0] == CMD_RENICE)
697 err_str = "%s tried to renice to %ld job id %ld for %s : "
699 else if (cmd[0] == CMD_SEND_SIGNAL)
700 err_str = "%s tried to send signal %ld to id %ld for %s : "
703 err_str = "cannot run unknown cmd with arg %ld on job id "
704 "%ld for %s : not allowed.";
706 warn(err_str, client->fcl_user, cmd[1], cmd[2],
708 Send_err_msg_end(fd, err_job_nfound_str);
711 /* request is valid : do it */
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);
718 Send_err_msg_end(fd, err_cmd_unknown_str);
719 exe_list_end_iteration(exe_list);
728 if (cmd[0] == CMD_RENICE)
729 err_str = "cannot renice job id %ld for %s : no corresponding "
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.";
735 err_str = "cannot run unknown cmd on job id %ld for %s :"
736 " no corresponding running job.";
738 warn(err_str, cmd[2], client->fcl_user);
739 Send_err_msg_end(fd, err_rjob_nfound_str);
742 Tell_no_more_data(fd);
749 cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
751 /* change nice value of a running job */
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);
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);
772 send_msg_fd(fd, "Command successfully completed on process %d.",
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);
781 #endif /* HAVE_SETPRIORITY */
786 cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e)
787 /* send a signal to a running job */
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);
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);
803 send_msg_fd(fd, "Command successfully completed on process %d.",
811 cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root)
812 /* Run a job and rescheduled if requested */
815 struct job_t *j = NULL;
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
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);
827 run_normal_job(j->j_line, fd);
829 if (cmd[0] == CMD_RUNNOW)
830 set_next_exe(j->j_line, FROM_CUR_NEXTEXE, fd);
832 Tell_no_more_data(fd);
840 /* we don't come here if a job has been found */
841 Send_err_msg_end(fd, err_job_nfound_str);
846 exe_cmd(struct fcrondyn_cl *client)
847 /* read command, and call corresponding function */
853 is_root = (strcmp(client->fcl_user, ROOTNAME) == 0) ? 1 : 0;
855 /* to make things clearer (avoid repeating client->fcl_ all the time) */
856 cmd = client->fcl_cmd;
857 fd = client->fcl_sock_fd;
860 debug("exe_cmd [0,1,2] : %d %d %d", cmd[0], cmd[1], cmd[2]);
865 case CMD_SEND_SIGNAL:
867 cmd_on_exeq(client, cmd, fd, is_root);
873 case CMD_LIST_SERIALQ:
875 cmd_ls(client, cmd, fd, is_root);
880 cmd_run(client, cmd, fd, is_root);
884 Send_err_msg_end(fd, err_cmd_unknown_str);
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 */
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);
901 *client = fcrondyn_cl_base;
904 prev_client->fcl_next = (*client)->fcl_next;
905 Free_safe((*client)->fcl_user);
907 *client = prev_client->fcl_next;
909 fcrondyn_cl_num -= 1;
913 check_socket(int num)
914 /* check for new connection, command, connection closed */
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];
921 struct fcrondyn_cl *client = NULL, *prev_client = NULL;
924 /* no socket to check : go directly to the end of that function */
927 debug("Checking socket ...");
929 if (FD_ISSET(listen_fd, &read_set)) {
930 debug("got new connection ...");
931 fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
934 ("could not accept new connection : isset(listen_fd = %d) = %d",
935 listen_fd, FD_ISSET(listen_fd, &read_set));
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) {
942 ("Could not set fd attribute O_NONBLOCK : connection rejected.");
943 shutdown(fd, SHUT_RDWR);
944 xclose_check(&fd, "client fd");
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;
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 */
960 add_to_select_set(fd, &master_set, &set_max_fd);
961 fcrondyn_cl_num += 1;
963 debug("Added connection fd : %d - %d connections", fd,
967 auth_client_so_peercred(client);
968 #elif defined(HAVE_GETPEERUCRED) || defined(HAVE_GETPEEREID)
969 auth_client_getpeer(client);
970 #endif /* SO_PEERCRED */
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.",
984 remove_connection(&client, prev_client);
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);
992 /* nothing to do on this one ... check the next one */
993 prev_client = client;
994 client = client->fcl_next;
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);
1006 error_e("error recv() from sock fd %d", client->fcl_sock_fd);
1007 prev_client = client;
1008 client = client->fcl_next;
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);
1018 /* we've just read a command ... */
1019 client->fcl_idle_since = now;
1022 prev_client = client;
1023 client = client->fcl_next;
1028 /* copy master_set in read_set, because read_set is modified by select() */
1029 read_set = master_set;
1035 /* close connections, close socket, remove socket file */
1037 struct fcrondyn_cl *client, *client_buf = NULL;
1040 shutdown(listen_fd, SHUT_RDWR);
1041 xclose_check(&listen_fd, "listening fd");
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");
1049 client_buf = client->fcl_next;
1051 fcrondyn_cl_num -= 1;
1052 client = client_buf;