]> err.no Git - backuppcd/blob - backuppcd-client.c
Import gnulib stuff
[backuppcd] / backuppcd-client.c
1 #include "compat.h"
2 #include "backuppcd-common.h"
3 #include <libbackuppcd.h>
4
5 /* XXX: include safety */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <stdio.h>
10
11 #define BPC_CLIENT_LIST 0
12 #define BPC_CLIENT_GET_INCR 1
13 #define BPC_CLIENT_GET_FULL 2
14
15 static char *bpc_hashstr(const unsigned char hash[16]) {
16         static char ret[33];
17         int i;
18
19         for (i = 0; i < 16; i++) {
20                 sprintf(ret + (i * 2), "%02x", hash[i]);
21         }
22
23         return(ret);
24 }
25
26 static char *bpc_poolpath(const char *pooldir, const unsigned char hash[16], int collidx) {
27         static char ret[8192];
28         char *hashstr;
29         int snprintf_ret;
30
31         hashstr = bpc_hashstr(hash);
32
33         if (collidx < 0) {
34                 snprintf_ret = snprintf(ret, sizeof(ret), "%s/%s", pooldir, hashstr);
35         } else {
36                 snprintf_ret = snprintf(ret, sizeof(ret), "%s/%s_%i", pooldir, hashstr, collidx);
37         }
38
39         if (snprintf_ret >= sizeof(ret)) {
40                 return(NULL);
41         }
42
43         return(ret);
44 }
45
46 static char *bpc_mungepath(const char *path) {
47         static char ret[8192];
48         char *path_cp, *path_cp_s;
49         char *retptr, *tmpbuf;
50
51         if (!path) {
52                 return(NULL);
53         }
54
55         path_cp_s = path_cp = strdup(path);
56         retptr = ret;
57         ret[0] = '\0';
58
59         if (path[0] == '/') {
60                 strcat(retptr, "f%2f");
61                 retptr += 4;
62         }
63
64         for (; (tmpbuf = strsep(&path_cp, "/")) != NULL;) {
65                 if (tmpbuf[0] == '\0') {
66                         continue;
67                 }
68
69                 *retptr = '/'; retptr++;
70                 *retptr = 'f'; retptr++;
71                 *retptr = '\0';
72                 strcat(retptr, tmpbuf);
73                 retptr += strlen(tmpbuf);
74         }
75
76         if (*retptr == '/') {
77                 retptr--;
78         }
79         *retptr = '\0';
80
81         free(path_cp_s);
82
83         return(ret);
84 }
85
86 static int bpc_fileuptodate(const char *pooldir, const char *datadir, struct bpc_fileinfo *finfo) {
87         struct stat stbuf;
88         char localfile[8192];
89         char *poolfile;
90         ino_t localinode, poolinode;
91         int retval = 0;
92         int snprintf_ret, stat_ret;
93         int i;
94
95         snprintf_ret = snprintf(localfile, sizeof(localfile), "%s/%s", datadir, bpc_mungepath(finfo->name));
96         if (snprintf_ret >= sizeof(localfile)) {
97                 CHECKPOINT;
98                 return(0);
99         }
100
101         /*
102          * If the local file does not exist, do no further checking.
103          */
104         stat_ret = stat(localfile, &stbuf);
105         if (stat_ret < 0) {
106                 CHECKPOINT;
107                 return(0);
108         }
109
110         localinode = stbuf.st_ino;
111
112         /*
113          * Iterate through all the available pool files to see if one matches
114          * our local file's inode
115          */
116         for (i = -1; i < 0xffff; i++) {
117                 poolfile = bpc_poolpath(pooldir, finfo->hash_bpc, i);
118
119                 stat_ret = stat(poolfile, &stbuf);
120                 if (stat_ret < 0) {
121                         CHECKPOINT;
122                         break;
123                 }
124
125                 poolinode = stbuf.st_ino;
126
127                 if (poolinode == localinode) {
128                         CHECKPOINT;
129                         retval = 1;
130                         break;
131                 }
132         }
133
134         CHECKPOINT;
135         return(retval);
136 }
137 static int bpc_updatenewfilelist(FILE *fp, struct bpc_fileinfo *finfo) {
138         if (fp == NULL || finfo == NULL) {
139                 return(-1);
140         }
141
142         /* XXX: Check to make sure finfo->hash_bpc isn't non-sense */
143         
144
145         /*
146          * Print out the required data
147          */
148         fprintf(fp, "%s %llu %s\n", bpc_hashstr(finfo->hash_bpc), finfo->size, bpc_mungepath(finfo->name));
149
150         return(0);
151 }
152
153 static int bpc_updatexferlog(FILE *fp, struct bpc_fileinfo *finfo, ...) {
154         return(0);
155 }
156
157 static int bpc_updateattrib(const char *pathname, struct bpc_fileinfo *finfo) {
158         static FILE *fp;
159         static char *lastdir = NULL;
160         size_t pathname_len;
161         char pathnameparent[16384], attribfile[16384], *tmp;
162         int snprintf_ret;
163         int needopen;
164
165         if (pathname == NULL || finfo == NULL) {
166                 return(-1);
167         }
168
169         pathname_len = strlen(pathname) + 1;
170
171         if (pathname_len >= sizeof(pathnameparent)) {
172                 return(-1);
173         }
174
175         memcpy(pathnameparent, pathname, pathname_len);
176
177         tmp = strrchr(pathnameparent, '/');
178         if (!tmp) {
179                 return(-1);
180         }
181
182         *tmp = '\0';
183
184         needopen = 1;
185         if (lastdir) {
186                 if (strcmp(lastdir, pathnameparent) == 0) {
187                         needopen = 0;
188                 }
189         }
190
191         if (needopen) {
192                 snprintf_ret = snprintf(attribfile, sizeof(attribfile), "%s/attrib", lastdir);
193                 if (snprintf_ret >= sizeof(attribfile)) {
194                         return(-1);
195                 }
196
197                 if (lastdir) {
198                         free(lastdir);
199                 }
200                 if (fp) {
201                         fclose(fp);
202                 }
203
204                 lastdir = strdup(pathnameparent);
205                 fp = fopen(attribfile, "w");
206         }
207
208         /* XXX: Write to attrib file */
209
210         return(0);
211 }
212
213 static int print_help(const char *msg) {
214         if (msg) {
215                 fprintf(stderr, "%s\n", msg);
216         }
217
218         return(0);
219 }
220
221 int main(int argc, char **argv) {
222         BPC_CONN *conn1, *conn2 = NULL;
223         struct bpc_fileinfo *finfo = NULL;
224         char *host = NULL, *command, *username = "anonymous", *password = NULL;
225         char localfile[16384];
226         int snprintf_ret;
227         int port = BPC_TCP_PORT;
228         int mode;
229         int ret = 0;
230         char *run_curr = "new", *run_last = NULL;
231         int lc_ret;
232         int uptodate;
233         char type_string[][7] = {"dir", "file", "syml", "sock", "fifo", "blk", "chr", "hrdl"};
234         char *pooldir = NULL;
235         char *datadir = NULL;
236         char lastdir[16384], currdir[16384];
237         char newfilelistname[16384], xferfilename[16384];
238         FILE *newfilelistfp = NULL;
239
240 #ifdef HAVE_LIBCONFIG
241         lc_register_var("Host", LC_VAR_STRING, &host, 'H');
242         lc_register_var("Port", LC_VAR_INT, &port, 'P');
243         lc_register_var("User", LC_VAR_STRING, &username, 'U');
244         lc_register_var("Pass", LC_VAR_STRING, &password, 'p');
245         lc_register_var("Pool", LC_VAR_STRING, &pooldir, 'S');
246         lc_register_var("DataDir", LC_VAR_STRING, &datadir, 'D');
247         lc_register_var("LastRun", LC_VAR_STRING, &run_last, 'r');
248         lc_register_var("CurrRun", LC_VAR_STRING, &run_curr, 'R');
249
250         lc_ret = lc_process(argc, argv, "backuppcdc", LC_CONF_SPACE, NULL);
251
252         lc_cleanup();
253
254         if (lc_ret != 0) {
255                 fprintf(stderr, "Error processing configuration: %s\n", lc_geterrstr());
256                 return(EXIT_FAILURE);
257         }
258
259         if (host == NULL) {
260                 print_help("Host is required");
261                 return(EXIT_FAILURE);
262         }
263         if (password == NULL) {
264                 print_help("Password is required");
265                 return(EXIT_FAILURE);
266         }
267         if (port < 0) {
268                 print_help("Port is required");
269                 return(EXIT_FAILURE);
270         }
271         if (argc == lc_optind) {
272                 print_help("Command is required");
273                 return(EXIT_FAILURE);
274         }
275
276         command = argv[lc_optind];
277 #endif
278
279         if (strcasecmp(command, "list") == 0) {
280                 mode = BPC_CLIENT_LIST;
281         } else if (strcasecmp(command, "get") == 0) {
282                 mode = BPC_CLIENT_GET_INCR;
283         } else if (strcasecmp(command, "getfull") == 0) {
284                 mode = BPC_CLIENT_GET_FULL;
285         } else {
286                 fprintf(stderr, "Invalid operation: %s\n", command);
287                 return(EXIT_FAILURE);
288         }
289
290         switch (mode) {
291                 case BPC_CLIENT_GET_INCR:
292                         if (run_last == NULL) {
293                                 print_help("Last Run is required");
294                                 return(EXIT_FAILURE);
295                         }
296                         if (pooldir == NULL) {
297                                 print_help("PoolDir is required");
298                                 return(EXIT_FAILURE);
299                         }
300
301                 case BPC_CLIENT_GET_FULL:
302                         if (datadir == NULL) {
303                                 print_help("DataDir is required");
304                                 return(EXIT_FAILURE);
305                         }
306
307                         if (run_last != NULL) {
308                                 snprintf(lastdir, sizeof(lastdir), "%s/%s", datadir, run_last);
309                         }
310                         snprintf(currdir, sizeof(currdir), "%s/%s", datadir, run_curr);
311                         snprintf(newfilelistname, sizeof(newfilelistname), "%s/NewFileList", datadir);
312                         snprintf(xferfilename, sizeof(xferfilename), "%s/XferLOG", datadir);
313                         newfilelistfp = fopen(newfilelistname, "w");
314         }
315
316         conn1 = bpc_connect(host, port, username, password);
317         if (!conn1) {
318                 fprintf(stderr, "Failed (connect)\n");
319                 return(EXIT_FAILURE);
320         }
321
322         /*
323          * Begin operation
324          */
325         switch (mode) {
326                 case BPC_CLIENT_LIST:
327                         ret = bpc_list_open(conn1, "/", 1, BPC_HASH_NONE, NULL, NULL);
328                         break;
329                 case BPC_CLIENT_GET_INCR:
330                         ret = bpc_list_open(conn1, "/", 1, BPC_HASH_BPC, NULL, NULL);
331
332                         conn2 = bpc_connect(host, port, username, password);
333                         if (!conn2) {
334                                 fprintf(stderr, "Failed (connect)\n");
335                                 return(EXIT_FAILURE);
336                         }
337                         break;
338                 case BPC_CLIENT_GET_FULL:
339                         ret = bpc_get_open(conn1, "/", 1, BPC_HASH_NONE, NULL, NULL);
340                         break;
341         }
342
343         if (!ret) {
344                 fprintf(stderr, "Failed (open)\n");
345                 return(EXIT_FAILURE);
346         }
347
348         /*
349          * Process every entry.
350          */
351         while (1) {
352                 switch (mode) {
353                         case BPC_CLIENT_LIST:
354                                 finfo = bpc_list(conn1);
355                                 break;
356                         case BPC_CLIENT_GET_INCR:
357                                 finfo = bpc_list(conn1);
358                                 break;
359                         case BPC_CLIENT_GET_FULL:
360                                 finfo = bpc_get_head(conn1);
361                                 break;
362                 }
363
364                 if (!finfo) {
365                         printf("--- end ---\n");
366                         break;
367                 }
368
369                 printf("[%4s] %06o %6lu %6lu %10llu %12lu (%s) %s",
370                        type_string[finfo->type],
371                        (unsigned int) finfo->mode,
372                        (unsigned long) finfo->uid,
373                        (unsigned long) finfo->gid,
374                        (unsigned long long) finfo->size,
375                        (unsigned long) finfo->mtime,
376                        bpc_hashstr(finfo->hash_bpc),
377                        finfo->name);
378                 if (finfo->type == BPC_FILE_SYMLINK || finfo->type == BPC_FILE_HRDLINK) {
379                         printf(" -> %s", finfo->linkdest);
380                 }
381                 printf("\n");
382
383                 if (mode == BPC_CLIENT_LIST) {
384                         continue;
385                 }
386
387                 snprintf_ret = snprintf(localfile, sizeof(localfile), "%s/%s", currdir, bpc_mungepath(finfo->name));
388                 if (snprintf_ret < 0 || snprintf_ret >= sizeof(localfile)) {
389                         PRINTERR("Filename too long.  Something is almost definitely wrong, aborting.");
390                         ret = 0;
391                         CHECKPOINT;
392                         break;
393                 }
394
395                 switch (mode) {
396                         case BPC_CLIENT_GET_INCR:
397                                 if (finfo->type == BPC_FILE_DIR) {
398                                         backuppc_mkdir(localfile);
399                                 }
400
401                                 /*
402                                  * Update attrib file
403                                  */
404                                 bpc_updateattrib(currdir, finfo);
405
406                                 if (finfo->type != BPC_FILE_REG) {
407                                         break;
408                                 }
409
410                                 /*
411                                  * Check to see if the file is up-to-date
412                                  */
413                                 uptodate = bpc_fileuptodate(pooldir, lastdir, finfo);
414                                 if (uptodate) {
415                                         /* If so, don't copy it again. */
416                                         break;
417                                 }
418
419                                 /*
420                                  * Update NewFileList
421                                  */
422                                 bpc_updatenewfilelist(newfilelistfp, finfo);
423
424                                 CHECKPOINT;
425                                 SPOTVAR_S(finfo->name);
426                                 ret = bpc_get_open(conn2, finfo->name, 0, BPC_HASH_NONE, NULL, NULL);
427                                 CHECKPOINT;
428                                 if (!ret) {
429                                         CHECKPOINT;
430                                         break;
431                                 }
432
433                                 CHECKPOINT;
434                                 finfo = bpc_get_head(conn2);
435                                 CHECKPOINT;
436                                 if (!finfo) {
437                                         ret = bpc_get_close(conn2);
438                                         CHECKPOINT;
439                                         break;
440                                 }
441
442                                 ret = bpc_copyfile(conn2, finfo, localfile, 0);
443                                 if (!ret) {
444                                         CHECKPOINT;
445                                 }
446
447                                 finfo = bpc_get_head(conn2);
448                                 if (finfo != NULL) {
449                                         ret = 0;
450                                         break;
451                                 }
452
453                                 ret = bpc_get_close(conn2);
454                                 CHECKPOINT;
455                                 break;
456                         case BPC_CLIENT_GET_FULL:
457                                 ret = bpc_copyfile(conn1, finfo, localfile, 0);
458
459                                 CHECKPOINT;
460                                 break;
461                 }
462
463                 if (!ret) {
464                         printf("---- failed during file copy ----\n");
465                         break;
466                 }
467         }
468
469         /*
470          * Cleanup
471          */
472         switch (mode) {
473                 case BPC_CLIENT_LIST:
474                         ret = bpc_list_close(conn1);
475                         break;
476                 case BPC_CLIENT_GET_INCR:
477                         ret = bpc_list_close(conn1);
478                         break;
479                 case BPC_CLIENT_GET_FULL:
480                         ret = bpc_get_close(conn1);
481                         break;
482         }
483
484         if (!ret) {
485                 fprintf(stderr, "Failed (close).\n");
486                 return(EXIT_FAILURE);
487         }
488
489         bpc_disconnect(conn1);
490         if (conn2) {
491                 bpc_disconnect(conn2);
492         }
493
494         fprintf(stderr, "Done.\n");
495         return(EXIT_SUCCESS);
496 }