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.
28 extern char debug_opt;
30 int write_buf_to_disk(int fd, char *write_buf, int *buf_used);
31 int save_type(int fd, short int type, char *write_buf, int *buf_used);
32 int save_str(int fd, short int type, char *str, char *write_buf, int *buf_used);
33 int save_strn(int fd, short int type, char *str, short int size, char *write_buf,
35 int save_lint(int fd, short int type, long int value, char *write_buf, int *buf_used);
36 int save_one_file(cf_t *file, char *filename, uid_t own_uid, gid_t own_gid,
41 save_type(int fd, short int type, char *write_buf, int *buf_used)
42 /* save a single type (with no data attached) in a binary fcrontab file */
45 int write_len = sizeof(type) + sizeof(size);
47 if ( write_len > WRITE_BUF_LEN - *buf_used )
48 if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
51 memcpy((write_buf+*buf_used), &type, sizeof(type));
52 *buf_used += sizeof(type);
53 memcpy((write_buf+*buf_used), &size, sizeof(size));
54 *buf_used += sizeof(size);
61 save_str(int fd, short int type, char *str, char *write_buf, int *buf_used)
62 /* save a string of type "type" in a binary fcrontab file */
64 short int size = strlen(str);
65 int write_len = sizeof(type) + sizeof(size) + size;
67 if ( write_len > WRITE_BUF_LEN - *buf_used )
68 if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
71 memcpy((write_buf+*buf_used), &type, sizeof(type));
72 *buf_used += sizeof(type);
73 memcpy((write_buf+*buf_used), &size, sizeof(size));
74 *buf_used += sizeof(size);
75 memcpy((write_buf+*buf_used), str, size);
82 save_strn(int fd, short int type, char *str, short int size, char *write_buf,
84 /* save a "size"-length string of type "type" in a binary fcrontab file */
86 int write_len = sizeof(type) + sizeof(size) + size;
88 if ( write_len > WRITE_BUF_LEN - *buf_used )
89 if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
92 memcpy((write_buf+*buf_used), &type, sizeof(type));
93 *buf_used += sizeof(type);
94 memcpy((write_buf+*buf_used), &size, sizeof(size));
95 *buf_used += sizeof(size);
96 memcpy((write_buf+*buf_used), str, size);
103 save_lint(int fd, short int type, long int value, char *write_buf, int *buf_used)
104 /* save an integer of type "type" in a binary fcrontab file */
106 short int size = sizeof(value);
107 int write_len = sizeof(type) + sizeof(size) + size;
109 if ( write_len > WRITE_BUF_LEN - *buf_used )
110 if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
113 memcpy((write_buf+*buf_used), &type, sizeof(type));
114 *buf_used += sizeof(type);
115 memcpy((write_buf+*buf_used), &size, sizeof(size));
116 *buf_used += sizeof(size);
117 memcpy((write_buf+*buf_used), &value, size);
125 write_buf_to_disk(int fd, char *write_buf, int *buf_used)
126 /* write the buffer to disk */
128 ssize_t to_write = *buf_used;
133 while ( written < to_write ) {
134 if ( num_retries++ > (int)(to_write / 2) ) {
135 error("too many retries (%d) to write buf to disk : giving up.",num_retries);
138 return_val = write(fd, (write_buf+written), to_write - written);
139 if ( return_val == -1 ) {
140 error_e("could not write() buf to disk");
143 written += return_val;
147 debug("write_buf_to_disk() : written %d/%d, %d (re)try(ies)", written, to_write,
151 if ( written == to_write ) {
156 error("write_buf_to_disk() : written %d bytes for %d requested.",
163 /* write_file_to_disk() error management */
164 #define Save_type(FD, TYPE, BUF, BUF_USED) \
166 if ( save_type(FD, TYPE, BUF, BUF_USED) != OK ) { \
167 error_e("Could not write type : file %s has not been saved.", \
173 #define Save_str(FD, TYPE, STR, BUF, BUF_USED) \
175 if ( save_str(FD, TYPE, STR, BUF, BUF_USED) != OK ) { \
176 error_e("Could not write str : file %s has not been saved.", \
182 #define Save_strn(FD, TYPE, STR, SIZE, BUF, BUF_USED) \
184 if ( save_strn(FD, TYPE, STR, SIZE, BUF, BUF_USED) != OK ) { \
185 error_e("Could not write strn : file %s has not been saved.", \
191 #define Save_lint(FD, TYPE, VALUE, BUF, BUF_USED) \
193 if ( save_lint(FD, TYPE, VALUE, BUF, BUF_USED) != OK ) { \
194 error_e("Could not write lint : file %s has not been saved.", \
201 write_file_to_disk(int fd, struct cf_t *file, time_t time_date)
202 /* write the data on the disk */
206 char write_buf[WRITE_BUF_LEN];
207 int write_buf_used = 0;
209 /* put program's version : it permits to daemon not to load
210 * a file which he won't understand the syntax, for exemple
211 * a file using a depreciated format generated by an old fcrontab,
212 * if the syntax has changed */
213 /* an binary fcrontab *must* start by such a header */
214 Save_lint(fd, S_HEADER_T, S_FILEVERSION, write_buf, &write_buf_used);
216 /* put the user's name : needed to check if his uid has not changed */
217 /* S_USER_T *must* be the 2nd field of a binary fcrontab */
218 Save_str(fd, S_USER_T, file->cf_user, write_buf, &write_buf_used);
220 /* put the time & date of saving : this is use for calcutating
221 * the system down time. As it is a new file, we set it to 0 */
222 /* S_USER_T *must* be the 3rd field of a binary fcrontab */
223 Save_lint(fd, S_TIMEDATE_T, time_date, write_buf, &write_buf_used);
225 /* Save the time diff between local (real) and system hour (if any) */
226 if ( file->cf_tzdiff != 0 )
227 Save_lint(fd, S_TZDIFF_T, file->cf_tzdiff, write_buf, &write_buf_used);
230 for (env = env_list_first(file->cf_env_list);
232 env = env_list_next(file->cf_env_list)) {
233 Save_str(fd, S_ENVVAR_T, env->e_envvar, write_buf, &write_buf_used);
237 for (line = file->cf_line_base; line; line = line->cl_next) {
239 /* this ones are saved for every lines */
240 Save_str(fd, S_SHELL_T, line->cl_shell, write_buf, &write_buf_used);
241 Save_str(fd, S_RUNAS_T, line->cl_runas, write_buf, &write_buf_used);
242 Save_str(fd, S_MAILTO_T, line->cl_mailto, write_buf, &write_buf_used);
243 Save_strn(fd, S_OPTION_T, (char *)line->cl_option, OPTION_SIZE,
244 write_buf, &write_buf_used);
245 Save_lint(fd, S_NEXTEXE_T, line->cl_nextexe, write_buf, &write_buf_used);
247 /* the following are saved only if needed */
248 if ( line->cl_numexe )
249 Save_strn(fd, S_NUMEXE_T, (char *)&line->cl_numexe, 1, write_buf, &write_buf_used);
250 if ( is_lavg(line->cl_option) )
251 Save_strn(fd, S_LAVG_T, (char *)line->cl_lavg, LAVG_SIZE,
252 write_buf, &write_buf_used);
253 if ( line->cl_until > 0 )
254 Save_lint(fd, S_UNTIL_T, line->cl_until, write_buf, &write_buf_used);
255 if ( line->cl_nice != 0 )
256 Save_strn(fd, S_NICE_T, &line->cl_nice, 1, write_buf, &write_buf_used);
257 if ( line->cl_runfreq > 0 ) {
258 Save_lint(fd, S_RUNFREQ_T, line->cl_runfreq, write_buf, &write_buf_used);
259 Save_lint(fd, S_REMAIN_T, line->cl_remain, write_buf, &write_buf_used);
261 if ( line->cl_tz != NULL ) {
262 Save_str(fd, S_TZ_T, line->cl_tz, write_buf, &write_buf_used);
264 if ( line->cl_jitter > 0 ) {
265 Save_lint(fd, S_JITTER_T, line->cl_jitter, write_buf, &write_buf_used);
268 if ( is_freq(line->cl_option) ) {
269 /* save the frequency to run the line */
270 Save_lint(fd, S_FIRST_T, line->cl_first, write_buf, &write_buf_used);
271 Save_lint(fd, S_TIMEFREQ_T, line->cl_timefreq, write_buf, &write_buf_used);
274 /* save the time and date bit fields */
275 Save_strn(fd, S_MINS_T, (char *)line->cl_mins, bitstr_size(60),
276 write_buf, &write_buf_used);
277 Save_strn(fd, S_HRS_T, (char *)line->cl_hrs, bitstr_size(24),
278 write_buf, &write_buf_used);
279 Save_strn(fd, S_DAYS_T, (char *)line->cl_days, bitstr_size(32),
280 write_buf, &write_buf_used);
281 Save_strn(fd, S_MONS_T, (char *)line->cl_mons, bitstr_size(12),
282 write_buf, &write_buf_used);
283 Save_strn(fd, S_DOW_T, (char *)line->cl_dow, bitstr_size(8),
284 write_buf, &write_buf_used);
287 /* This field *must* be the last of each line */
288 Save_type(fd, S_ENDLINE_T, write_buf, &write_buf_used);
291 if ( write_buf_to_disk(fd, write_buf, &write_buf_used) == ERR ) {
292 error("Could not write final buffer content to disk: file %s has not been saved.", file->cf_user);
301 save_one_file(cf_t *file, char *filename, uid_t own_uid, gid_t own_gid, time_t save_date)
302 /* save a given file to disk */
308 if ( is_selinux_enabled() && setfscreatecon(file->cf_file_context) )
310 error_e("Could not set create context for file %s", filename);
314 fd = open_as_user(filename, own_uid, own_gid, O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, S_IRUSR|S_IWUSR);
316 if ( is_selinux_enabled() )
317 setfscreatecon(NULL);
320 error_e("Could not open %s", filename);
324 if (fchown(fd, own_uid, own_gid) != 0) {
325 error_e("Could not fchown %s to uid:%d gid:%d", filename, own_uid, own_gid);
327 error_e("save_one_file(%s): could not close(fd)", filename);
328 remove_as_user(filename, own_uid, own_gid);
333 if ( write_file_to_disk(fd, file, save_date) == ERR ) {
335 error_e("save_one_file(%s): could not close(fd)", filename);
336 remove_as_user(filename, own_uid, own_gid);
341 error_e("save_one_file(%s): could not close(fd)", filename);
348 save_file_safe(cf_t *file, char *final_path, char *prog_name, uid_t own_uid,
349 gid_t own_gid, time_t save_date)
350 /* save a file to a temp path, and then rename it (safely) to avoid loss of data
351 * if a system crash, hardware failure, etc happens. */
353 char temp_path[PATH_LEN+4];
354 int final_path_len, temp_path_index;
355 char *tmp_str = ".tmp";
357 final_path_len = strlen(final_path);
358 strncpy(temp_path, final_path, sizeof(temp_path)-sizeof(tmp_str));
359 temp_path_index = ( final_path_len > sizeof(temp_path)-sizeof(tmp_str) ) ?
360 sizeof(temp_path)-sizeof(tmp_str) : final_path_len;
361 strcpy(&temp_path[temp_path_index], tmp_str);
363 if ( save_one_file(file, temp_path, own_uid, own_gid, save_date) == OK ) {
364 if ( rename_as_user(temp_path, final_path, own_uid, own_gid) != 0 ) {
365 error_e("Cannot rename %s to %s", temp_path, final_path);
366 error("%s will try to save the name to its definitive filename "
367 "directly.", prog_name);
368 error("If there is an error, root may consider to replace %s (which is "
369 "a valid copy) by %s manually.", final_path, temp_path);
370 if ( save_one_file(file, final_path, own_uid, own_gid, save_date) == ERR )
375 error("Since %s has not been able to save %s's file, it will keep "
376 "the previous version (if any) of %s.", prog_name, final_path, final_path);