Line data Source code
1 : #include "../burp.h"
2 : #include "../action.h"
3 : #include "../asfd.h"
4 : #include "../async.h"
5 : #include "../cmd.h"
6 : #include "../cntr.h"
7 : #include "../handy.h"
8 : #include "../fsops.h"
9 : #include "../iobuf.h"
10 : #include "../lock.h"
11 : #include "../log.h"
12 : #include "../regexp.h"
13 : #include "../run_script.h"
14 : #include "main.h"
15 : #include "backup.h"
16 : #include "delete.h"
17 : #include "diff.h"
18 : #include "list.h"
19 : #include "restore.h"
20 : #include "rubble.h"
21 : #include "sdirs.h"
22 : #include "run_action.h"
23 : #include "timestamp.h"
24 :
25 : // FIX THIS: Somewhat haphazard.
26 : /* Return 0 for everything OK. -1 for error, or 1 to mean that there was
27 : another process that has the lock. */
28 2 : static int get_lock_sdirs_for_write(struct asfd *asfd, struct sdirs *sdirs)
29 : {
30 : struct stat statp;
31 :
32 : // Make sure the lock directory exists.
33 2 : if(mkpath(&sdirs->lock_storage_for_write->path, sdirs->lockdir))
34 : {
35 0 : asfd->write_str(asfd, CMD_ERROR, "problem with lock directory");
36 0 : goto error;
37 : }
38 :
39 2 : lock_get(sdirs->lock_storage_for_write);
40 2 : switch(sdirs->lock_storage_for_write->status)
41 : {
42 : case GET_LOCK_GOT: break;
43 : case GET_LOCK_NOT_GOT:
44 0 : if(!lstat(sdirs->finishing, &statp))
45 : {
46 0 : char msg[256]="";
47 0 : logp("finalising previous backup\n");
48 0 : snprintf(msg, sizeof(msg),
49 : "Finalising previous backup of client. "
50 : "Please try again later.");
51 0 : asfd->write_str(asfd, CMD_ERROR, msg);
52 : }
53 : else
54 : {
55 0 : logp("Another instance of client is already running.\n");
56 0 : asfd->write_str(asfd, CMD_ERROR,
57 : "another instance is already running");
58 : }
59 : goto lockedout;
60 : case GET_LOCK_ERROR:
61 : default:
62 0 : logp("Problem with lock file on server: %s\n",
63 : sdirs->lock_storage_for_write->path);
64 0 : asfd->write_str(asfd, CMD_ERROR,
65 : "problem with lock file on server");
66 0 : goto error;
67 : }
68 :
69 : return 0;
70 : lockedout:
71 : return 1;
72 : error:
73 : return -1;
74 : }
75 :
76 : static int client_can_generic(struct conf **cconfs, enum conf_opt o)
77 : {
78 5 : return get_int(cconfs[o]);
79 : }
80 :
81 0 : int client_can_monitor(struct conf **cconfs)
82 : {
83 0 : return client_can_generic(cconfs, OPT_CLIENT_CAN_MONITOR);
84 : }
85 :
86 1 : static int client_can_restore(struct conf **cconfs)
87 : {
88 1 : const char *restore_path=get_string(cconfs[OPT_RESTORE_PATH]);
89 :
90 : // If there is a restore file on the server, it is always OK.
91 1 : if(restore_path && is_reg_lstat(restore_path)==1)
92 : {
93 : // Remove the file.
94 0 : unlink(restore_path);
95 0 : return 1;
96 : }
97 :
98 1 : return client_can_generic(cconfs, OPT_CLIENT_CAN_RESTORE);
99 : }
100 :
101 1 : void maybe_do_notification(struct asfd *asfd,
102 : int status, const char *clientdir,
103 : const char *storagedir, const char *filename,
104 : const char *brv, struct conf **cconfs)
105 : {
106 1 : int a=0;
107 : const char *args[12];
108 1 : struct cntr *cntr=get_cntr(cconfs);
109 1 : args[a++]=NULL; // Fill in the script name later.
110 1 : args[a++]=get_string(cconfs[OPT_CNAME]);
111 1 : args[a++]=clientdir;
112 1 : args[a++]=storagedir;
113 1 : args[a++]=filename;
114 1 : args[a++]=brv;
115 1 : if(status)
116 : {
117 1 : args[0]=get_string(cconfs[OPT_N_FAILURE_SCRIPT]);
118 1 : args[a++]="0";
119 1 : args[a++]=NULL;
120 1 : run_script(asfd, args, get_strlist(cconfs[OPT_N_FAILURE_ARG]),
121 : cconfs, 1, 1, 1);
122 : }
123 0 : else if((get_int(cconfs[OPT_N_SUCCESS_WARNINGS_ONLY])
124 0 : && cntr->ent[CMD_WARNING]->count > 0)
125 0 : || (get_int(cconfs[OPT_N_SUCCESS_CHANGES_ONLY])
126 0 : && cntr->ent[CMD_TOTAL]->changed > 0)
127 0 : || (!get_int(cconfs[OPT_N_SUCCESS_WARNINGS_ONLY])
128 0 : && !get_int(cconfs[OPT_N_SUCCESS_CHANGES_ONLY])))
129 : {
130 0 : char warnings[32]="";
131 0 : snprintf(warnings, sizeof(warnings), "%" PRIu64,
132 0 : cntr->ent[CMD_WARNING]->count);
133 0 : args[0]=get_string(cconfs[OPT_N_SUCCESS_SCRIPT]);
134 0 : args[a++]=warnings;
135 0 : args[a++]=NULL;
136 0 : run_script(asfd, args, get_strlist(cconfs[OPT_N_SUCCESS_ARG]),
137 : cconfs, 1, 1, 1);
138 : }
139 1 : }
140 :
141 18 : static int parse_restore_str(
142 : const char *str,
143 : enum action *act,
144 : int *input,
145 : char **backupnostr,
146 : char **restoreregex
147 : ) {
148 18 : int ret=-1;
149 18 : char *cp=NULL;
150 18 : char *copy=NULL;
151 :
152 18 : if(!str)
153 : {
154 1 : logp("NULL passed to %s\n", __func__);
155 1 : goto end;
156 : }
157 :
158 17 : if(!(copy=strdup_w(str, __func__)))
159 : goto end;
160 :
161 17 : if(!strncmp_w(copy, "restore "))
162 7 : *act=ACTION_RESTORE;
163 10 : else if(!strncmp_w(copy, "verify "))
164 7 : *act=ACTION_VERIFY;
165 : else
166 : {
167 3 : logp("Could not parse %s in %s\n", copy, __func__);
168 3 : goto end;
169 : }
170 :
171 14 : if(!(cp=strchr(copy, ' ')))
172 : {
173 0 : logp("Could not parse %s in %s\n", copy, __func__);
174 0 : goto end;
175 : }
176 14 : cp++;
177 14 : *input=0;
178 14 : if(!strncmp_w(cp, "restore_list "))
179 : {
180 6 : cp+=strlen("restore_list ");
181 6 : *input=1;
182 : }
183 14 : if(!(*backupnostr=strdup_w(cp, __func__)))
184 : goto end;
185 14 : if((cp=strchr(*backupnostr, ':')))
186 : {
187 8 : *cp='\0';
188 8 : cp++;
189 8 : if(!(*restoreregex=strdup_w(cp, __func__)))
190 : goto end;
191 : }
192 :
193 : ret=0;
194 : end:
195 18 : free_w(©);
196 18 : return ret;
197 : }
198 :
199 : #ifndef UTEST
200 : static
201 : #endif
202 18 : int parse_restore_str_and_set_confs(const char *str, enum action *act,
203 : struct conf **cconfs)
204 : {
205 18 : int ret=-1;
206 18 : int input=0;
207 18 : char *backupnostr=NULL;
208 18 : char *restoreregex=NULL;
209 :
210 18 : if(parse_restore_str(str, act, &input, &backupnostr, &restoreregex))
211 : goto end;
212 :
213 14 : if(set_string(cconfs[OPT_RESTORE_LIST], input?"":NULL))
214 : goto end;
215 14 : if(set_string(cconfs[OPT_BACKUP], backupnostr))
216 : goto end;
217 14 : if(restoreregex && *restoreregex
218 4 : && set_string(cconfs[OPT_REGEX], restoreregex))
219 : goto end;
220 : ret=0;
221 : end:
222 18 : free_w(&backupnostr);
223 18 : free_w(&restoreregex);
224 18 : return ret;
225 : }
226 :
227 2 : static int run_restore(struct asfd *asfd,
228 : struct sdirs *sdirs, struct conf **cconfs, int srestore)
229 : {
230 2 : int ret=-1;
231 2 : char *dir_for_notify=NULL;
232 2 : enum action act=ACTION_RESTORE;
233 2 : struct iobuf *rbuf=asfd->rbuf;
234 2 : const char *cname=get_string(cconfs[OPT_CNAME]);
235 :
236 2 : if(parse_restore_str_and_set_confs(rbuf->buf, &act, cconfs))
237 : goto end;
238 :
239 2 : iobuf_free_content(rbuf);
240 :
241 2 : if(act==ACTION_RESTORE)
242 : {
243 : int r;
244 1 : if((r=client_can_restore(cconfs))<0)
245 : goto end;
246 1 : else if(!r)
247 : {
248 0 : logp("Not allowing restore of %s\n", cname);
249 0 : if(!asfd->write_str(asfd, CMD_GEN,
250 0 : "Client restore is not allowed")) ret=0;
251 : goto end;
252 : }
253 : }
254 2 : if(act==ACTION_VERIFY
255 1 : && !(client_can_generic(cconfs, OPT_CLIENT_CAN_VERIFY)))
256 : {
257 0 : logp("Not allowing verify of %s\n", cname);
258 0 : if(!asfd->write_str(asfd, CMD_GEN,
259 0 : "Client verify is not allowed")) ret=0;
260 : goto end;
261 : }
262 :
263 2 : if(get_string(cconfs[OPT_RESTORE_LIST]))
264 : {
265 : // Should start receiving the input file here.
266 0 : if(asfd->write_str(asfd, CMD_GEN, "ok restore_list"))
267 : goto end;
268 0 : if(receive_a_file(asfd, sdirs->restore_list, get_cntr(cconfs)))
269 : {
270 : goto end;
271 : }
272 : }
273 : else
274 : {
275 2 : if(asfd->write_str(asfd, CMD_GEN, "ok"))
276 : goto end;
277 : }
278 :
279 2 : ret=do_restore_server(asfd, sdirs, act,
280 : srestore, &dir_for_notify, cconfs);
281 2 : if(dir_for_notify)
282 0 : maybe_do_notification(asfd, ret,
283 0 : sdirs->client, dir_for_notify,
284 : act==ACTION_RESTORE?"restorelog":"verifylog",
285 0 : act==ACTION_RESTORE?"restore":"verify",
286 : cconfs);
287 : end:
288 2 : free_w(&dir_for_notify);
289 2 : return ret;
290 : }
291 :
292 1 : static int run_delete(struct asfd *asfd,
293 : struct sdirs *sdirs, struct conf **cconfs)
294 : {
295 1 : char *backupno=NULL;
296 1 : struct iobuf *rbuf=asfd->rbuf;
297 1 : const char *cname=get_string(cconfs[OPT_CNAME]);
298 1 : if(!client_can_generic(cconfs, OPT_CLIENT_CAN_DELETE))
299 : {
300 0 : logp("Not allowing delete of %s\n", cname);
301 0 : asfd->write_str(asfd, CMD_GEN, "Client delete is not allowed");
302 0 : return -1;
303 : }
304 1 : backupno=rbuf->buf+strlen("delete ");
305 1 : return do_delete_server(asfd, sdirs,
306 : cconfs, cname, backupno,
307 1 : get_string(cconfs[OPT_MANUAL_DELETE]));
308 : }
309 :
310 1 : static int run_list(struct asfd *asfd,
311 : struct sdirs *sdirs, struct conf **cconfs)
312 : {
313 1 : int ret=-1;
314 1 : char *cp=NULL;
315 1 : char *backupno=NULL;
316 1 : char *browsedir=NULL;
317 1 : char *listregex=NULL;
318 1 : struct iobuf *rbuf=asfd->rbuf;
319 :
320 1 : if(!client_can_generic(cconfs, OPT_CLIENT_CAN_LIST))
321 : {
322 0 : logp("Not allowing list of %s\n",
323 : get_string(cconfs[OPT_CNAME]));
324 0 : asfd->write_str(asfd, CMD_GEN, "Client list is not allowed");
325 0 : goto end;
326 : }
327 :
328 1 : if(!strncmp_w(rbuf->buf, "list "))
329 : {
330 1 : if((cp=strchr(rbuf->buf, ':')))
331 : {
332 0 : *cp='\0';
333 0 : if(!(listregex=strdup_w(cp+1, __func__)))
334 : goto end;
335 : }
336 1 : if(!(backupno=strdup_w(rbuf->buf+strlen("list "), __func__)))
337 : goto end;
338 :
339 : }
340 0 : else if(!strncmp_w(rbuf->buf, "listb "))
341 : {
342 0 : if((cp=strchr(rbuf->buf, ':')))
343 : {
344 0 : *cp='\0';
345 0 : if(!(browsedir=strdup_w(cp+1, __func__)))
346 : goto end;
347 : }
348 0 : strip_trailing_slashes(&browsedir);
349 0 : if(!(backupno=strdup_w(rbuf->buf+strlen("listb "), __func__)))
350 : goto end;
351 : }
352 1 : if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end;
353 :
354 1 : iobuf_free_content(asfd->rbuf);
355 :
356 1 : if(list_server_init(asfd, sdirs, cconfs,
357 : backupno, listregex, browsedir))
358 : goto end;
359 1 : ret=do_list_server();
360 : end:
361 1 : free_w(&backupno);
362 1 : free_w(&browsedir);
363 1 : free_w(&listregex);
364 1 : list_server_free();
365 1 : return ret;
366 : }
367 :
368 1 : static int run_diff(struct asfd *asfd,
369 : struct sdirs *sdirs, struct conf **cconfs)
370 : {
371 1 : int ret=-1;
372 1 : char *backup1=NULL;
373 1 : char *backup2=NULL;
374 1 : struct iobuf *rbuf=asfd->rbuf;
375 :
376 1 : if(!client_can_generic(cconfs, OPT_CLIENT_CAN_DIFF))
377 : {
378 0 : logp("Not allowing diff of %s\n",
379 : get_string(cconfs[OPT_CNAME]));
380 0 : asfd->write_str(asfd, CMD_GEN, "Client diff is not allowed");
381 0 : goto end;
382 : }
383 :
384 1 : if(!strncmp_w(rbuf->buf, "diff "))
385 : {
386 : char *cp;
387 1 : if((cp=strchr(rbuf->buf, ':')))
388 : {
389 0 : *cp='\0';
390 0 : if(!(backup2=strdup_w(cp+1, __func__)))
391 : goto end;
392 : }
393 1 : if(!(backup1=strdup_w(rbuf->buf+strlen("diff "), __func__)))
394 : goto end;
395 : }
396 1 : if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end;
397 :
398 1 : iobuf_free_content(asfd->rbuf);
399 :
400 1 : ret=do_diff_server(asfd, sdirs, cconfs, backup1, backup2);
401 : end:
402 1 : free_w(&backup1);
403 1 : free_w(&backup2);
404 1 : return ret;
405 : }
406 :
407 : static int unknown_command(struct asfd *asfd, const char *func)
408 : {
409 2 : iobuf_log_unexpected(asfd->rbuf, func);
410 2 : asfd->write_str(asfd, CMD_ERROR, "unknown command");
411 : return -1;
412 : }
413 :
414 0 : static const char *buf_to_notify_str(struct iobuf *rbuf)
415 : {
416 0 : const char *buf=rbuf->buf;
417 0 : if(!strncmp_w(buf, "backup")) return "backup";
418 0 : else if(!strncmp_w(buf, "delete")) return "delete";
419 0 : else if(!strncmp_w(buf, "diff")) return "diff";
420 0 : else if(!strncmp_w(buf, "list")) return "list";
421 0 : else if(!strncmp_w(buf, "restore")) return "restore";
422 0 : else if(!strncmp_w(buf, "verify")) return "verify";
423 : else return "unknown";
424 : }
425 :
426 9 : static int maybe_write_first_created_file(struct sdirs *sdirs,
427 : const char *tstmp)
428 : {
429 9 : if(is_reg_lstat(sdirs->created)>0
430 9 : || is_lnk_lstat(sdirs->current)>0
431 9 : || is_lnk_lstat(sdirs->currenttmp)>0
432 9 : || is_lnk_lstat(sdirs->working)>0
433 9 : || is_lnk_lstat(sdirs->finishing)>0)
434 : return 0;
435 :
436 9 : return timestamp_write(sdirs->created, tstmp);
437 : }
438 :
439 9 : static int log_command(struct async *as,
440 : struct sdirs *sdirs, struct conf **cconfs, const char *tstmp)
441 : {
442 9 : struct fzp *fzp=NULL;
443 9 : struct asfd *asfd=as->asfd;
444 9 : struct iobuf *rbuf=asfd->rbuf;
445 9 : char *cname=get_string(cconfs[OPT_CONNECT_CLIENT]);
446 :
447 9 : if(rbuf->cmd!=CMD_GEN)
448 : return 0;
449 :
450 8 : if(!(fzp=fzp_open(sdirs->command, "a")))
451 : return -1;
452 8 : fzp_printf(fzp, "%s %s %s %s\n", tstmp, asfd->peer_addr, cname,
453 : iobuf_to_printable(rbuf));
454 8 : if(fzp_close(&fzp))
455 : return -1;
456 :
457 : return 0;
458 : }
459 :
460 9 : static int run_action_server_do(struct async *as, struct sdirs *sdirs,
461 : const char *incexc, int srestore, int *timer_ret, struct conf **cconfs)
462 : {
463 : int max_parallel_backups;
464 9 : int working=0;
465 : int ret;
466 9 : int resume=0;
467 9 : char msg[256]="";
468 9 : char tstmp[48]="";
469 9 : struct iobuf *rbuf=as->asfd->rbuf;
470 :
471 : // Make sure some directories exist.
472 9 : if(mkpath(&sdirs->current, sdirs->dedup))
473 : {
474 0 : snprintf(msg, sizeof(msg),
475 : "could not mkpath %s", sdirs->current);
476 0 : log_and_send(as->asfd, msg);
477 0 : return -1;
478 : }
479 :
480 9 : if(timestamp_get_new(/*index*/0,
481 : tstmp, sizeof(tstmp),
482 : /*bufforfile*/NULL, /*bs*/0,
483 : /*format*/NULL))
484 : return -1;
485 :
486 : // Carry on if these fail, otherwise you will not be able to restore
487 : // from readonly backups.
488 9 : maybe_write_first_created_file(sdirs, tstmp);
489 9 : log_command(as, sdirs, cconfs, tstmp);
490 :
491 9 : if(rbuf->cmd!=CMD_GEN)
492 2 : return unknown_command(as->asfd, __func__);
493 :
494 : // List and diff should work well enough without needing to lock
495 : // anything.
496 8 : if(!strncmp_w(rbuf->buf, "list ")
497 7 : || !strncmp_w(rbuf->buf, "listb "))
498 1 : return run_list(as->asfd, sdirs, cconfs);
499 :
500 7 : if(!strncmp_w(rbuf->buf, "diff "))
501 1 : return run_diff(as->asfd, sdirs, cconfs);
502 :
503 : // Old clients will send 'delete', possibly accidentally due to the
504 : // user trying to use the new diff/long diff options.
505 : // Stop them from working, just to be safe.
506 6 : if(!strncmp_w(rbuf->buf, "delete "))
507 : {
508 1 : logp("old style delete from %s denied\n",
509 : get_string(cconfs[OPT_CNAME]));
510 1 : as->asfd->write_str(as->asfd, CMD_ERROR,
511 : "old style delete is not supported on this server");
512 1 : return -1;
513 : }
514 :
515 : // Restore and verify should work well enough by locking only the
516 : // backup directory they are interested in.
517 5 : if(!strncmp_w(rbuf->buf, "restore ")
518 4 : || !strncmp_w(rbuf->buf, "verify "))
519 : {
520 2 : ret=run_restore(as->asfd, sdirs, cconfs, srestore);
521 2 : unlink(sdirs->restore_list);
522 2 : return ret;
523 : }
524 :
525 3 : if(strncmp_w(rbuf->buf, "backup")
526 2 : && strncmp_w(rbuf->buf, "Delete "))
527 2 : return unknown_command(as->asfd, __func__);
528 :
529 : // Beyond this point, only need to deal with backup and delete.
530 : // These require locking out all other backups and deletes.
531 :
532 2 : switch((ret=get_lock_sdirs_for_write(as->asfd, sdirs)))
533 : {
534 : case 0: break; // OK.
535 : case 1: return 1; // Locked out.
536 : default: // Error.
537 0 : maybe_do_notification(as->asfd, ret,
538 : "", "error in get_lock_sdirs()",
539 : "", buf_to_notify_str(rbuf), cconfs);
540 0 : return -1;
541 : }
542 :
543 2 : switch((ret=check_for_rubble_and_clean(as, sdirs,
544 : incexc, &resume, cconfs)))
545 : {
546 : case 0: break; // OK.
547 : case 1: return 1; // Now finalising.
548 : default: // Error.
549 0 : maybe_do_notification(as->asfd, ret,
550 : "", "error in check_for_rubble()",
551 : "", buf_to_notify_str(rbuf), cconfs);
552 0 : return -1;
553 : }
554 :
555 2 : if(!strncmp_w(rbuf->buf, "Delete "))
556 1 : return run_delete(as->asfd, sdirs, cconfs);
557 :
558 : // Only backup action left to deal with.
559 1 : working = server_get_working(NULL);
560 1 : max_parallel_backups = get_int(cconfs[OPT_MAX_PARALLEL_BACKUPS]);
561 :
562 1 : logp("%d/%d working (cur/max)\n", working, max_parallel_backups);
563 :
564 1 : if(max_parallel_backups && working >= max_parallel_backups)
565 : {
566 0 : struct asfd *asfd=as->asfd;
567 0 : logp("max parallel backups reached\n");
568 0 : return asfd->write_str(asfd, CMD_GEN, "max parallel backups");
569 : }
570 :
571 1 : ret=run_backup(as, sdirs,
572 : cconfs, incexc, timer_ret, resume);
573 :
574 : // If this is a backup failure and the client has more servers
575 : // to failover to, do not notify.
576 1 : if(ret
577 1 : && get_int(cconfs[OPT_N_FAILURE_BACKUP_FAILOVERS_LEFT])
578 1 : && get_int(cconfs[OPT_BACKUP_FAILOVERS_LEFT]))
579 : return ret;
580 :
581 1 : if(*timer_ret<0)
582 0 : maybe_do_notification(as->asfd, ret,
583 : "", "error running timer script",
584 : "", "backup", cconfs);
585 1 : else if(!*timer_ret)
586 1 : maybe_do_notification(as->asfd, ret,
587 1 : sdirs->client, sdirs->current,
588 : "log", "backup", cconfs);
589 : return ret;
590 : }
591 :
592 9 : int run_action_server(struct async *as,
593 : const char *incexc, int srestore, int *timer_ret, struct conf **cconfs)
594 : {
595 9 : int ret=-1;
596 9 : struct sdirs *sdirs=NULL;
597 9 : if((sdirs=sdirs_alloc())
598 9 : && !sdirs_init_from_confs(sdirs, cconfs))
599 9 : ret=run_action_server_do(as,
600 : sdirs, incexc, srestore, timer_ret, cconfs);
601 9 : if(sdirs) lock_release(sdirs->lock_storage_for_write);
602 9 : sdirs_free(&sdirs);
603 9 : return ret;
604 : }
|