]> granicus.if.org Git - fcron/blob - save.c
install-restart: use service (works with systemd) instead of /etc/init.d/fcron
[fcron] / save.c
1 /*
2  * FCRON - periodic command scheduler 
3  *
4  *  Copyright 2000-2014 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 #include "global.h"
26 #include "save.h"
27
28 extern char debug_opt;
29
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,
34               char *write_buf, int *buf_used);
35 int save_lint(int fd, short int type, long int value, char *write_buf,
36               int *buf_used);
37 int save_one_file(cf_t * file, char *filename, uid_t own_uid, gid_t own_gid,
38                   time_t save_date);
39
40
41 int
42 save_type(int fd, short int type, char *write_buf, int *buf_used)
43 /* save a single type (with no data attached) in a binary fcrontab file */
44 {
45     short int size = 0;
46     int write_len = sizeof(type) + sizeof(size);
47
48     if (write_len > WRITE_BUF_LEN - *buf_used)
49         if (write_buf_to_disk(fd, write_buf, buf_used) == ERR)
50             return ERR;
51
52     memcpy((write_buf + *buf_used), &type, sizeof(type));
53     *buf_used += sizeof(type);
54     memcpy((write_buf + *buf_used), &size, sizeof(size));
55     *buf_used += sizeof(size);
56
57     return OK;
58
59 }
60
61 int
62 save_str(int fd, short int type, char *str, char *write_buf, int *buf_used)
63 /* save a string of type "type" in a binary fcrontab file */
64 {
65     short int size = (str != NULL) ? strlen(str) : 0;
66     int write_len = sizeof(type) + sizeof(size) + size;
67
68     if (write_len > WRITE_BUF_LEN - *buf_used)
69         if (write_buf_to_disk(fd, write_buf, buf_used) == ERR)
70             return ERR;
71
72     memcpy((write_buf + *buf_used), &type, sizeof(type));
73     *buf_used += sizeof(type);
74     memcpy((write_buf + *buf_used), &size, sizeof(size));
75     *buf_used += sizeof(size);
76     memcpy((write_buf + *buf_used), str, size);
77     *buf_used += size;
78
79     return OK;
80 }
81
82 int
83 save_strn(int fd, short int type, char *str, short int size, char *write_buf,
84           int *buf_used)
85 /* save a "size"-length string of type "type" in a binary fcrontab file */
86 {
87     int write_len = sizeof(type) + sizeof(size) + size;
88
89     if (write_len > WRITE_BUF_LEN - *buf_used)
90         if (write_buf_to_disk(fd, write_buf, buf_used) == ERR)
91             return ERR;
92
93     memcpy((write_buf + *buf_used), &type, sizeof(type));
94     *buf_used += sizeof(type);
95     memcpy((write_buf + *buf_used), &size, sizeof(size));
96     *buf_used += sizeof(size);
97     memcpy((write_buf + *buf_used), str, size);
98     *buf_used += size;
99
100     return OK;
101 }
102
103 int
104 save_lint(int fd, short int type, long int value, char *write_buf,
105           int *buf_used)
106 /* save an integer of type "type" in a binary fcrontab file */
107 {
108     short int size = sizeof(value);
109     int write_len = sizeof(type) + sizeof(size) + size;
110
111     if (write_len > WRITE_BUF_LEN - *buf_used)
112         if (write_buf_to_disk(fd, write_buf, buf_used) == ERR)
113             return ERR;
114
115     memcpy((write_buf + *buf_used), &type, sizeof(type));
116     *buf_used += sizeof(type);
117     memcpy((write_buf + *buf_used), &size, sizeof(size));
118     *buf_used += sizeof(size);
119     memcpy((write_buf + *buf_used), &value, size);
120     *buf_used += size;
121
122     return OK;
123 }
124
125
126 int
127 write_buf_to_disk(int fd, char *write_buf, int *buf_used)
128 /* write the buffer to disk */
129 {
130     ssize_t to_write = *buf_used;
131     ssize_t written = 0;
132     ssize_t return_val;
133     int num_retries = 0;
134
135     while (written < to_write) {
136         if (num_retries++ > (int)(to_write / 2)) {
137             error("too many retries (%d) to write buf to disk : giving up.",
138                   num_retries);
139             return ERR;
140         }
141         return_val = write(fd, (write_buf + written), to_write - written);
142         if (return_val == -1) {
143             error_e("could not write() buf to disk");
144             return ERR;
145         }
146         written += return_val;
147     }
148
149     /* */
150     debug("write_buf_to_disk() : written %d/%d, %d (re)try(ies)", written,
151           to_write, num_retries);
152     /* */
153
154     if (written == to_write) {
155         *buf_used = 0;
156         return OK;
157     }
158     else {
159         error("write_buf_to_disk() : written %d bytes for %d requested.",
160               written, to_write);
161         return ERR;
162     }
163 }
164
165
166 /* write_file_to_disk() error management */
167 #define Save_type(FD, TYPE, BUF, BUF_USED) \
168         { \
169           if ( save_type(FD, TYPE, BUF, BUF_USED) != OK ) { \
170             error_e("Could not write type : file %s has not been saved.", \
171                      file->cf_user); \
172             return ERR; \
173           } \
174         }
175
176 #define Save_str(FD, TYPE, STR, BUF, BUF_USED) \
177         { \
178           if ( save_str(FD, TYPE, STR, BUF, BUF_USED) != OK ) { \
179             error_e("Could not write str : file %s has not been saved.", \
180                      file->cf_user); \
181             return ERR; \
182           } \
183         }
184
185 #define Save_strn(FD, TYPE, STR, SIZE, BUF, BUF_USED) \
186         { \
187           if ( save_strn(FD, TYPE, STR, SIZE, BUF, BUF_USED) != OK ) { \
188             error_e("Could not write strn : file %s has not been saved.", \
189                      file->cf_user); \
190             return ERR; \
191           } \
192         }
193
194 #define Save_lint(FD, TYPE, VALUE, BUF, BUF_USED) \
195         { \
196           if ( save_lint(FD, TYPE, VALUE, BUF, BUF_USED) != OK ) { \
197             error_e("Could not write lint : file %s has not been saved.", \
198                      file->cf_user); \
199             return ERR; \
200           } \
201         }
202
203 int
204 write_file_to_disk(int fd, struct cf_t *file, time_t time_date)
205 /* write the data on the disk */
206 {
207     cl_t *line = NULL;
208     env_t *env = NULL;
209     char write_buf[WRITE_BUF_LEN];
210     int write_buf_used = 0;
211
212     /* put program's version : it permits to daemon not to load
213      * a file which he won't understand the syntax, for exemple
214      * a file using a depreciated format generated by an old fcrontab,
215      * if the syntax has changed */
216     /* an binary fcrontab *must* start by such a header */
217     Save_lint(fd, S_HEADER_T, S_FILEVERSION, write_buf, &write_buf_used);
218
219     /* put the user's name : needed to check if his uid has not changed */
220     /* S_USER_T *must* be the 2nd field of a binary fcrontab */
221     Save_str(fd, S_USER_T, file->cf_user, write_buf, &write_buf_used);
222
223     /* put the time & date of saving : this is use for calcutating 
224      * the system down time. As it is a new file, we set it to 0 */
225     /* S_USER_T *must* be the 3rd field of a binary fcrontab */
226     Save_lint(fd, S_TIMEDATE_T, time_date, write_buf, &write_buf_used);
227
228     /* Save the time diff between local (real) and system hour (if any) */
229     if (file->cf_tzdiff != 0)
230         Save_lint(fd, S_TZDIFF_T, file->cf_tzdiff, write_buf, &write_buf_used);
231
232     /*   env variables, */
233     for (env = env_list_first(file->cf_env_list);
234          env != NULL; env = env_list_next(file->cf_env_list)) {
235         Save_str(fd, S_ENVVAR_T, env->e_envvar, write_buf, &write_buf_used);
236     }
237
238     /*   then, lines. */
239     for (line = file->cf_line_base; line; line = line->cl_next) {
240
241         /* this ones are saved for every lines */
242         Save_str(fd, S_SHELL_T, line->cl_shell, write_buf, &write_buf_used);
243         Save_str(fd, S_RUNAS_T, line->cl_runas, write_buf, &write_buf_used);
244         Save_str(fd, S_MAILTO_T, line->cl_mailto, write_buf, &write_buf_used);
245         Save_strn(fd, S_OPTION_T, (char *)line->cl_option, OPTION_SIZE,
246                   write_buf, &write_buf_used);
247         Save_lint(fd, S_NEXTEXE_T, line->cl_nextexe, write_buf,
248                   &write_buf_used);
249
250         /* the following are saved only if needed */
251         if (line->cl_numexe)
252             Save_strn(fd, S_NUMEXE_T, (char *)&line->cl_numexe, 1, write_buf,
253                       &write_buf_used);
254         if (is_lavg(line->cl_option))
255             Save_strn(fd, S_LAVG_T, (char *)line->cl_lavg, LAVG_SIZE,
256                       write_buf, &write_buf_used);
257         if (line->cl_until > 0)
258             Save_lint(fd, S_UNTIL_T, line->cl_until, write_buf,
259                       &write_buf_used);
260         if (line->cl_nice != 0)
261             Save_strn(fd, S_NICE_T, &line->cl_nice, 1, write_buf,
262                       &write_buf_used);
263         if (line->cl_runfreq > 0) {
264             Save_lint(fd, S_RUNFREQ_T, line->cl_runfreq, write_buf,
265                       &write_buf_used);
266             Save_lint(fd, S_REMAIN_T, line->cl_remain, write_buf,
267                       &write_buf_used);
268         }
269         if (line->cl_tz != NULL) {
270             Save_str(fd, S_TZ_T, line->cl_tz, write_buf, &write_buf_used);
271         }
272         if (line->cl_jitter > 0) {
273             Save_lint(fd, S_JITTER_T, line->cl_jitter, write_buf,
274                       &write_buf_used);
275         }
276         if (line->cl_mailfrom != NULL && line->cl_mailfrom[0] != '\0') {
277             Save_str(fd, S_MAILFROM_T, line->cl_mailfrom, write_buf,
278                      &write_buf_used);
279         }
280
281         if (is_freq(line->cl_option)) {
282             /* save the frequency to run the line */
283             Save_lint(fd, S_FIRST_T, line->cl_first, write_buf,
284                       &write_buf_used);
285             Save_lint(fd, S_TIMEFREQ_T, line->cl_timefreq, write_buf,
286                       &write_buf_used);
287         }
288         else {
289             /* save the time and date bit fields */
290             Save_strn(fd, S_MINS_T, (char *)line->cl_mins, bitstr_size(60),
291                       write_buf, &write_buf_used);
292             Save_strn(fd, S_HRS_T, (char *)line->cl_hrs, bitstr_size(24),
293                       write_buf, &write_buf_used);
294             Save_strn(fd, S_DAYS_T, (char *)line->cl_days, bitstr_size(32),
295                       write_buf, &write_buf_used);
296             Save_strn(fd, S_MONS_T, (char *)line->cl_mons, bitstr_size(12),
297                       write_buf, &write_buf_used);
298             Save_strn(fd, S_DOW_T, (char *)line->cl_dow, bitstr_size(8),
299                       write_buf, &write_buf_used);
300         }
301
302         /* This field *must* be the last of each line */
303         Save_type(fd, S_ENDLINE_T, write_buf, &write_buf_used);
304     }
305
306     if (write_buf_to_disk(fd, write_buf, &write_buf_used) == ERR) {
307         error
308             ("Could not write final buffer content to disk: file %s has not been saved.",
309              file->cf_user);
310         return ERR;
311     }
312
313     /* Make sure the data is fully synchronize to disk before files are renamed 
314      * to their final destination.
315      * This is to avoid cases where the file name (meta-data) would be updated,
316      * and there is a crash before the data is fully written: not sure if that
317      * is possible, but better safe than sorry! */
318     if (fdatasync(fd) < 0) {
319         error_e("could not fdatasync() %s's fcrontab", file->cf_user);
320         return ERR;
321     }
322
323     return OK;
324 }
325
326
327 int
328 save_one_file(cf_t * file, char *filename, uid_t own_uid, gid_t own_gid,
329               time_t save_date)
330 /* save a given file to disk */
331 {
332     int fd;
333
334     /* open file */
335 #ifdef WITH_SELINUX
336     if (is_selinux_enabled() && setfscreatecon(file->cf_file_context)) {
337         error_e("Could not set create context for file %s", filename);
338         return ERR;
339     }
340 #endif
341     fd = open_as_user(filename, own_uid, own_gid,
342                       O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
343 #ifdef WITH_SELINUX
344     if (is_selinux_enabled())
345         setfscreatecon(NULL);
346 #endif
347     if (fd == -1) {
348         error_e("Could not open %s", filename);
349         return ERR;
350     }
351
352     if (fchown(fd, own_uid, own_gid) != 0) {
353         error_e("Could not fchown %s to uid:%d gid:%d", filename, own_uid,
354                 own_gid);
355         if (xclose(&fd) < 0)
356             error_e("save_one_file(%s): could not xclose(fd)", filename);
357         remove_as_user(filename, own_uid, own_gid);
358         return ERR;
359     }
360
361     /* save file : */
362     if (write_file_to_disk(fd, file, save_date) == ERR) {
363         if (xclose(&fd) < 0)
364             error_e("save_one_file(%s): could not xclose(fd)", filename);
365         remove_as_user(filename, own_uid, own_gid);
366         return ERR;
367     }
368
369     if (xclose(&fd) < 0)
370         error_e("save_one_file(%s): could not xclose(fd)", filename);
371
372     return OK;
373 }
374
375
376 int
377 save_file_safe(cf_t * file, char *final_path, char *prog_name, uid_t own_uid,
378                gid_t own_gid, time_t save_date)
379 /* save a file to a temp path, and then rename it (safely) to avoid loss of data
380  * if a system crash, hardware failure, etc happens. */
381 {
382     char temp_path[PATH_LEN + 4];
383     int final_path_len, temp_path_index;
384     char *tmp_str = ".tmp";
385
386     final_path_len = strlen(final_path);
387     strncpy(temp_path, final_path, sizeof(temp_path) - sizeof(tmp_str));
388     temp_path_index = (final_path_len > sizeof(temp_path) - sizeof(tmp_str)) ?
389         sizeof(temp_path) - sizeof(tmp_str) : final_path_len;
390     strcpy(&temp_path[temp_path_index], tmp_str);
391
392     if (save_one_file(file, temp_path, own_uid, own_gid, save_date) == OK) {
393         if (rename_as_user(temp_path, final_path, own_uid, own_gid) != 0) {
394             error_e("Cannot rename %s to %s", temp_path, final_path);
395             error("%s will try to save the name to its definitive filename "
396                   "directly.", prog_name);
397             error
398                 ("If there is an error, root may consider to replace %s (which is "
399                  "a valid copy) by %s manually.", final_path, temp_path);
400             if (save_one_file(file, final_path, own_uid, own_gid, save_date) ==
401                 ERR)
402                 return ERR;
403         }
404     }
405     else {
406         error("Since %s has not been able to save %s's file, it will keep "
407               "the previous version (if any) of %s.", prog_name, final_path,
408               final_path);
409         return ERR;
410     }
411
412     return OK;
413
414 }