]> granicus.if.org Git - fcron/blob - save.c
Updated to-do list
[fcron] / save.c
1 /*
2  * FCRON - periodic command scheduler 
3  *
4  *  Copyright 2000-2012 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, char *write_buf,
34               int *buf_used);
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,
37                   time_t save_date);
38
39
40 int
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 */
43 {
44     short int size = 0;
45     int write_len = sizeof(type) + sizeof(size);
46
47     if ( write_len > WRITE_BUF_LEN - *buf_used )
48         if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
49             return ERR;
50
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);
55
56     return OK;
57
58 }
59
60 int
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 */
63 {
64     short int size = strlen(str);
65     int write_len = sizeof(type) + sizeof(size) + size;
66
67     if ( write_len > WRITE_BUF_LEN - *buf_used )
68         if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
69             return ERR;
70
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);
76     *buf_used += size;
77
78     return OK;
79 }
80
81 int
82 save_strn(int fd, short int type, char *str, short int size, char *write_buf,
83           int *buf_used)
84 /* save a "size"-length string of type "type" in a binary fcrontab file */
85 {
86     int write_len = sizeof(type) + sizeof(size) + size;
87
88     if ( write_len > WRITE_BUF_LEN - *buf_used )
89         if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
90             return ERR;
91
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);
97     *buf_used += size;
98
99     return OK;
100 }
101
102 int
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 */
105 {
106     short int size = sizeof(value);
107     int write_len = sizeof(type) + sizeof(size) + size;
108
109     if ( write_len > WRITE_BUF_LEN - *buf_used )
110         if ( write_buf_to_disk(fd, write_buf, buf_used) == ERR )
111             return ERR;
112
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);
118     *buf_used += size;
119
120     return OK;
121 }
122
123
124 int
125 write_buf_to_disk(int fd, char *write_buf, int *buf_used)
126 /* write the buffer to disk */
127 {
128     ssize_t to_write = *buf_used; 
129     ssize_t written = 0;
130     ssize_t return_val;
131     int num_retries = 0;
132
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);
136             return ERR;
137         }
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");
141             return ERR;
142         }
143         written += return_val;
144     }
145
146     /* */
147     debug("write_buf_to_disk() : written %d/%d, %d (re)try(ies)", written, to_write, 
148           num_retries);
149     /* */
150
151     if ( written == to_write ) {
152         *buf_used = 0;
153         return OK;
154     }
155     else {
156         error("write_buf_to_disk() : written %d bytes for %d requested.",
157               written, to_write);
158         return ERR;
159     }
160 }
161
162
163 /* write_file_to_disk() error management */
164 #define Save_type(FD, TYPE, BUF, BUF_USED) \
165         { \
166           if ( save_type(FD, TYPE, BUF, BUF_USED) != OK ) { \
167             error_e("Could not write type : file %s has not been saved.", \
168                      file->cf_user); \
169             return ERR; \
170           } \
171         }
172
173 #define Save_str(FD, TYPE, STR, BUF, BUF_USED) \
174         { \
175           if ( save_str(FD, TYPE, STR, BUF, BUF_USED) != OK ) { \
176             error_e("Could not write str : file %s has not been saved.", \
177                      file->cf_user); \
178             return ERR; \
179           } \
180         }
181
182 #define Save_strn(FD, TYPE, STR, SIZE, BUF, BUF_USED) \
183         { \
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.", \
186                      file->cf_user); \
187             return ERR; \
188           } \
189         }
190
191 #define Save_lint(FD, TYPE, VALUE, BUF, BUF_USED) \
192         { \
193           if ( save_lint(FD, TYPE, VALUE, BUF, BUF_USED) != OK ) { \
194             error_e("Could not write lint : file %s has not been saved.", \
195                      file->cf_user); \
196             return ERR; \
197           } \
198         }
199
200 int
201 write_file_to_disk(int fd, struct cf_t *file, time_t time_date)
202 /* write the data on the disk */
203 {
204     cl_t *line = NULL;
205     env_t *env = NULL;
206     char write_buf[WRITE_BUF_LEN];
207     int write_buf_used = 0;
208
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);
215
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);
219
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);
224
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);
228
229     /*   env variables, */
230     for (env = env_list_first(file->cf_env_list);
231          env != NULL;
232          env = env_list_next(file->cf_env_list)) {
233         Save_str(fd, S_ENVVAR_T, env->e_envvar, write_buf, &write_buf_used);
234     }
235         
236     /*   then, lines. */
237     for (line = file->cf_line_base; line; line = line->cl_next) {
238
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);
246
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);
260         }
261         if ( line->cl_tz != NULL ) {
262             Save_str(fd, S_TZ_T, line->cl_tz, write_buf, &write_buf_used);
263         }
264         if ( line->cl_jitter > 0 ) {
265             Save_lint(fd, S_JITTER_T, line->cl_jitter, write_buf, &write_buf_used);
266         }
267                      
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);
272         }
273         else {
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);
285         }
286
287         /* This field *must* be the last of each line */
288         Save_type(fd, S_ENDLINE_T, write_buf, &write_buf_used);
289     }
290
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);
293         return ERR;
294     }
295
296     return OK;
297 }
298
299
300 int
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 */
303 {
304     int fd;
305
306     /* open file */
307 #ifdef WITH_SELINUX
308     if ( is_selinux_enabled() && setfscreatecon(file->cf_file_context) )
309     {
310         error_e("Could not set create context for file %s", filename);
311         return ERR;
312     }
313 #endif
314     fd = open_as_user(filename, own_uid, own_gid, O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, S_IRUSR|S_IWUSR);
315 #ifdef WITH_SELINUX
316     if ( is_selinux_enabled() )
317         setfscreatecon(NULL);
318 #endif
319     if ( fd == -1 ) {
320         error_e("Could not open %s", filename);
321         return ERR;
322     }
323
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);
326         if ( close(fd) < 0 )
327             error_e("save_one_file(%s): could not close(fd)", filename);
328         remove_as_user(filename, own_uid, own_gid);
329         return ERR;
330     }
331
332     /* save file : */
333     if ( write_file_to_disk(fd, file, save_date) == ERR ) {
334         if ( close(fd) < 0 )
335             error_e("save_one_file(%s): could not close(fd)", filename);
336         remove_as_user(filename, own_uid, own_gid);
337         return ERR;
338     }
339
340     if ( close(fd) < 0 )
341         error_e("save_one_file(%s): could not close(fd)", filename);
342
343     return OK;
344 }
345
346
347 int
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. */
352 {
353     char temp_path[PATH_LEN+4];
354     int final_path_len, temp_path_index;
355     char *tmp_str = ".tmp";
356
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);
362
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 )
371                 return ERR;
372         }
373     }
374     else {
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);
377         return ERR;
378     }
379
380     return OK;
381
382 }