1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * scan.c -- scanning routines for Bacula
21 *
22 * Kern Sibbald, MM separated from util.c MMIII
23 *
24 */
25
26
27 #include "bacula.h"
28 #include "jcr.h"
29 #include "findlib/find.h"
30
31 /* Strip leading space from command line arguments */
strip_leading_space(char * str)32 void strip_leading_space(char *str)
33 {
34 char *p = str;
35 while (B_ISSPACE(*p)) {
36 p++;
37 }
38 if (str != p) {
39 do {
40 *str++ = *p;
41 } while (*p++ != 0);
42 }
43 }
44
45 /* Strip any trailing junk from the command */
strip_trailing_junk(char * cmd)46 char *strip_trailing_junk(char *cmd)
47 {
48 char *p;
49
50 /* strip trailing junk from command */
51 p = cmd - 1 + strlen(cmd);
52 while ((p >= cmd) && (B_ISSPACE(*p) || *p == '\n' || *p == '\r')) {
53 *p-- = 0;
54 }
55 return cmd;
56 }
57
58 /* Strip any trailing newline characters from the string */
strip_trailing_newline(char * cmd)59 char *strip_trailing_newline(char *cmd)
60 {
61 char *p;
62 p = cmd - 1 + strlen(cmd);
63 while ((p >= cmd) && (*p == '\n' || *p == '\r')) *p-- = 0;
64 return cmd;
65 }
66
67 /* Strip any trailing slashes from a directory path */
strip_trailing_slashes(char * dir)68 char *strip_trailing_slashes(char *dir)
69 {
70 char *p;
71
72 /* strip trailing slashes */
73 p = dir -1 + strlen(dir);
74 while (p >= dir && IsPathSeparator(*p)) *p-- = 0;
75 return dir;
76 }
77
78 /*
79 * Skip spaces
80 * Returns: 0 on failure (EOF)
81 * 1 on success
82 * new address in passed parameter
83 */
skip_spaces(char ** msg)84 bool skip_spaces(char **msg)
85 {
86 char *p = *msg;
87 if (!p) {
88 return false;
89 }
90 while (*p && B_ISSPACE(*p)) {
91 p++;
92 }
93 *msg = p;
94 return *p ? true : false;
95 }
96
97 /*
98 * Skip nonspaces
99 * Returns: 0 on failure (EOF)
100 * 1 on success
101 * new address in passed parameter
102 */
skip_nonspaces(char ** msg)103 bool skip_nonspaces(char **msg)
104 {
105 char *p = *msg;
106
107 if (!p) {
108 return false;
109 }
110 while (*p && !B_ISSPACE(*p)) {
111 p++;
112 }
113 *msg = p;
114 return *p ? true : false;
115 }
116
117 /* folded search for string - case insensitive */
118 int
fstrsch(const char * a,const char * b)119 fstrsch(const char *a, const char *b) /* folded case search */
120 {
121 const char *s1,*s2;
122 char c1, c2;
123
124 s1=a;
125 s2=b;
126 while (*s1) { /* do it the fast way */
127 if ((*s1++ | 0x20) != (*s2++ | 0x20))
128 return 0; /* failed */
129 }
130 while (*a) { /* do it over the correct slow way */
131 if (B_ISUPPER(c1 = *a)) {
132 c1 = tolower((int)c1);
133 }
134 if (B_ISUPPER(c2 = *b)) {
135 c2 = tolower((int)c2);
136 }
137 if (c1 != c2) {
138 return 0;
139 }
140 a++;
141 b++;
142 }
143 return 1;
144 }
145
146
147 /*
148 * Return next argument from command line. Note, this
149 * routine is destructive because it stored 0 at the end
150 * of each argument.
151 * Called with pointer to pointer to command line. This
152 * pointer is updated to point to the remainder of the
153 * command line.
154 *
155 * Returns pointer to next argument -- don't store the result
156 * in the pointer you passed as an argument ...
157 * The next argument is terminated by a space unless within
158 * quotes. Double quote characters (unless preceded by a \) are
159 * stripped.
160 *
161 */
next_arg(char ** s)162 char *next_arg(char **s)
163 {
164 char *p, *q, *n;
165 bool in_quote = false;
166
167 /* skip past spaces to next arg */
168 for (p=*s; *p && B_ISSPACE(*p); ) {
169 p++;
170 }
171 Dmsg1(900, "Next arg=%s\n", p);
172 for (n = q = p; *p ; ) {
173 if (*p == '\\') { /* slash? */
174 p++; /* yes, skip it */
175 if (*p) {
176 *q++ = *p++;
177 } else {
178 *q++ = *p;
179 }
180 continue;
181 }
182 if (*p == '"') { /* start or end of quote */
183 p++;
184 in_quote = !in_quote; /* change state */
185 continue;
186 }
187 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
188 p++;
189 break;
190 }
191 *q++ = *p++;
192 }
193 *q = 0;
194 *s = p;
195 Dmsg2(900, "End arg=%s next=%s\n", n, p);
196 return n;
197 }
198
199 /*
200 * This routine parses the input command line.
201 * It makes a copy in args, then builds an
202 * argc, argk, argv list where:
203 *
204 * argc = count of arguments
205 * argk[i] = argument keyword (part preceding =)
206 * argv[i] = argument value (part after =)
207 *
208 * example: arg1 arg2=abc arg3=
209 *
210 * argc = c
211 * argk[0] = arg1
212 * argv[0] = NULL
213 * argk[1] = arg2
214 * argv[1] = abc
215 * argk[2] = arg3
216 * argv[2] =
217 */
parse_args(POOLMEM * cmd,POOLMEM ** args,int * argc,char ** argk,char ** argv,int max_args)218 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
219 char **argk, char **argv, int max_args)
220 {
221 char *p;
222
223 parse_args_only(cmd, args, argc, argk, argv, max_args);
224
225 /* Separate keyword and value */
226 for (int i=0; i < *argc; i++) {
227 p = strchr(argk[i], '=');
228 if (p) {
229 *p++ = 0; /* terminate keyword and point to value */
230 }
231 argv[i] = p; /* save ptr to value or NULL */
232 }
233 #ifdef xxx_debug
234 for (int i=0; i < *argc; i++) {
235 Pmsg3(000, "Arg %d: kw=%s val=%s\n", i, argk[i], argv[i]?argv[i]:"NULL");
236 }
237 #endif
238 return 1;
239 }
240
241
242 /*
243 * This routine parses the input command line.
244 * It makes a copy in args, then builds an
245 * argc, argk, but no argv (values).
246 * This routine is useful for scanning command lines where the data
247 * is a filename and no keywords are expected. If we scan a filename
248 * for keywords, any = in the filename will be interpreted as the
249 * end of a keyword, and this is not good.
250 *
251 * argc = count of arguments
252 * argk[i] = argument keyword (part preceding =)
253 * argv[i] = NULL
254 *
255 * example: arg1 arg2=abc arg3=
256 *
257 * argc = c
258 * argk[0] = arg1
259 * argv[0] = NULL
260 * argk[1] = arg2=abc
261 * argv[1] = NULL
262 * argk[2] = arg3
263 * argv[2] =
264 */
parse_args_only(POOLMEM * cmd,POOLMEM ** args,int * argc,char ** argk,char ** argv,int max_args)265 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
266 char **argk, char **argv, int max_args)
267 {
268 char *p, *n;
269
270 pm_strcpy(args, cmd);
271 strip_trailing_junk(*args);
272 p = *args;
273 *argc = 0;
274 /* Pick up all arguments */
275 while (*argc < max_args) {
276 n = next_arg(&p);
277 if (*n) {
278 argk[*argc] = n;
279 argv[(*argc)++] = NULL;
280 } else {
281 break;
282 }
283 }
284 return 1;
285 }
286
287
288 /*
289 * Given a full filename, split it into its path
290 * and filename parts. They are returned in pool memory
291 * in the arguments provided.
292 */
split_path_and_filename(const char * fname,POOLMEM ** path,int * pnl,POOLMEM ** file,int * fnl)293 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
294 POOLMEM **file, int *fnl)
295 {
296 const char *f;
297 int slen;
298 int len = slen = strlen(fname);
299
300 /*
301 * Find path without the filename.
302 * I.e. everything after the last / is a "filename".
303 * OK, maybe it is a directory name, but we treat it like
304 * a filename. If we don't find a / then the whole name
305 * must be a path name (e.g. c:).
306 */
307 f = fname + len - 1;
308 /* "strip" any trailing slashes */
309 while (slen > 1 && IsPathSeparator(*f)) {
310 slen--;
311 f--;
312 }
313 /* Walk back to last slash -- begin of filename */
314 while (slen > 0 && !IsPathSeparator(*f)) {
315 slen--;
316 f--;
317 }
318 if (IsPathSeparator(*f)) { /* did we find a slash? */
319 f++; /* yes, point to filename */
320 } else { /* no, whole thing must be path name */
321 f = fname;
322 }
323 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
324 *fnl = fname - f + len;
325 if (*fnl > 0) {
326 *file = check_pool_memory_size(*file, *fnl+1);
327 memcpy(*file, f, *fnl); /* copy filename */
328 }
329 (*file)[*fnl] = 0;
330
331 *pnl = f - fname;
332 if (*pnl > 0) {
333 *path = check_pool_memory_size(*path, *pnl+1);
334 memcpy(*path, fname, *pnl);
335 }
336 (*path)[*pnl] = 0;
337
338 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
339 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
340 }
341
342 /*
343 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
344 *
345 * Note, BIG is the default maximum length when no length
346 * has been specified for %s. If it is not big enough, then
347 * simply add a length such as %10000s.
348 */
349 const int BIG = 1000;
bsscanf(const char * buf,const char * fmt,...)350 int bsscanf(const char *buf, const char *fmt, ...)
351 {
352 va_list ap;
353 int count = 0;
354 void *vp;
355 char *cp;
356 int l = 0;
357 int max_len = BIG;
358 uint64_t value;
359 bool error = false;
360 bool negative;
361
362 va_start(ap, fmt);
363 while (*fmt && !error) {
364 // Dmsg1(000, "fmt=%c\n", *fmt);
365 if (*fmt == '%') {
366 fmt++;
367 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
368 switch_top:
369 switch (*fmt++) {
370 case 'u':
371 value = 0;
372 while (B_ISDIGIT(*buf)) {
373 value = B_TIMES10(value) + *buf++ - '0';
374 }
375 vp = (void *)va_arg(ap, void *);
376 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
377 if (l == 0) {
378 *((int *)vp) = (int)value;
379 } else if (l == 1) {
380 *((uint32_t *)vp) = (uint32_t)value;
381 // Dmsg0(000, "Store 32 bit int\n");
382 } else {
383 *((uint64_t *)vp) = (uint64_t)value;
384 // Dmsg0(000, "Store 64 bit int\n");
385 }
386 count++;
387 l = 0;
388 break;
389 case 'd':
390 value = 0;
391 if (*buf == '-') {
392 negative = true;
393 buf++;
394 } else {
395 negative = false;
396 }
397 while (B_ISDIGIT(*buf)) {
398 value = B_TIMES10(value) + *buf++ - '0';
399 }
400 if (negative) {
401 value = -value;
402 }
403 vp = (void *)va_arg(ap, void *);
404 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
405 if (l == 0) {
406 *((int *)vp) = (int)value;
407 } else if (l == 1) {
408 *((int32_t *)vp) = (int32_t)value;
409 // Dmsg0(000, "Store 32 bit int\n");
410 } else {
411 *((int64_t *)vp) = (int64_t)value;
412 // Dmsg0(000, "Store 64 bit int\n");
413 }
414 count++;
415 l = 0;
416 break;
417 case 'l':
418 // Dmsg0(000, "got l\n");
419 l = 1;
420 if (*fmt == 'l') {
421 l++;
422 fmt++;
423 }
424 if (*fmt == 'd' || *fmt == 'u') {
425 goto switch_top;
426 }
427 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
428 error = true;
429 break;
430 case 'q':
431 l = 2;
432 if (*fmt == 'd' || *fmt == 'u') {
433 goto switch_top;
434 }
435 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
436 error = true;
437 break;
438 case 's':
439 // Dmsg1(000, "Store string max_len=%d\n", max_len);
440 cp = (char *)va_arg(ap, char *);
441 while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
442 *cp++ = *buf++;
443 }
444 *cp = 0;
445 count++;
446 max_len = BIG;
447 break;
448 case 'c':
449 cp = (char *)va_arg(ap, char *);
450 *cp = *buf++;
451 count++;
452 break;
453 case '%':
454 if (*buf++ != '%') {
455 error = true;
456 }
457 break;
458 default:
459 fmt--;
460 max_len = 0;
461 while (B_ISDIGIT(*fmt)) {
462 max_len = B_TIMES10(max_len) + *fmt++ - '0';
463 }
464 // Dmsg1(000, "Default max_len=%d\n", max_len);
465 if (*fmt == 's') {
466 goto switch_top;
467 }
468 // Dmsg1(000, "Default c=%c\n", *fmt);
469 error = true;
470 break; /* error: unknown format */
471 }
472 continue;
473
474 /* White space eats zero or more whitespace */
475 } else if (B_ISSPACE(*fmt)) {
476 fmt++;
477 while (B_ISSPACE(*buf)) {
478 buf++;
479 }
480 /* Plain text must match */
481 } else if (*buf++ != *fmt++) {
482 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
483 error = true;
484 break;
485 }
486 }
487 va_end(ap);
488 // Dmsg2(000, "Error=%d count=%d\n", error, count);
489 if (error) {
490 count = -1;
491 }
492 return count;
493 }
494
495 /*
496 * Return next name from a comma separated list. Note, this
497 * routine is destructive because it stored 0 at the end
498 * of each argument.
499 * Called with pointer to pointer to command line. This
500 * pointer is updated to point to the remainder of the
501 * command line.
502 *
503 * Returns pointer to next name -- don't store the result
504 * in the pointer you passed as an argument ...
505 * The next argument is terminated by a , unless within
506 * quotes. Double quote characters (unless preceded by a \) are
507 * stripped.
508 *
509 */
next_name(char ** s)510 char *next_name(char **s)
511 {
512 char *p, *q, *n;
513 bool in_quote = false;
514
515 if (s == NULL || *s == NULL || **s == '\0') {
516 return NULL;
517 }
518 p = *s;
519 Dmsg1(900, "Next name=%s\n", p);
520 for (n = q = p; *p ; ) {
521 if (*p == '\\') { /* slash? */
522 p++; /* yes, skip it */
523 if (*p) {
524 *q++ = *p++;
525 } else {
526 *q++ = *p;
527 }
528 continue;
529 }
530 if (*p == '"') { /* start or end of quote */
531 p++;
532 in_quote = !in_quote; /* change state */
533 continue;
534 }
535 if (!in_quote && *p == ',') { /* end of field */
536 p++;
537 break;
538 }
539 *q++ = *p++;
540 }
541 *q = 0;
542 *s = p;
543 Dmsg2(900, "End arg=%s next=%s\n", n, p);
544 return n;
545 }
546
547 #ifdef TEST_PROGRAM
main(int argc,char * argv[])548 int main(int argc, char *argv[])
549 {
550 char buf[100];
551 uint32_t val32;
552 uint64_t val64;
553 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
554 char Job[200];
555 int cnt;
556 char *helloreq= "Hello *UserAgent* calling\n";
557 char *hello = "Hello %127s calling\n";
558 char *catreq =
559 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
560 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
561 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
562 "StartBlock=%u EndBlock=%u\n";
563 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
564 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
565 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
566 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
567 " VolReadTime=%" lld " VolWriteTime=%" lld;
568 char *media =
569 "1000 OK VolName=TestVolume001 VolJobs=0 VolFiles=0 VolBlocks=0 VolBytes=1 VolMounts=0 VolErrors=0 VolWrites=0 MaxVolBytes=0 VolCapacityBytes=0 VolStatus=Append Slot=0 MaxVolJobs=0 MaxVolFiles=0 InChanger=1 VolReadTime=0 VolWriteTime=0";
570 struct VOLUME_CAT_INFO {
571 /* Media info for the current Volume */
572 uint32_t VolCatJobs; /* number of jobs on this Volume */
573 uint32_t VolCatFiles; /* Number of files */
574 uint32_t VolCatBlocks; /* Number of blocks */
575 uint64_t VolCatBytes; /* Number of bytes written */
576 uint32_t VolCatMounts; /* Number of mounts this volume */
577 uint32_t VolCatErrors; /* Number of errors this volume */
578 uint32_t VolCatWrites; /* Number of writes this volume */
579 uint32_t VolCatReads; /* Number of reads this volume */
580 uint64_t VolCatRBytes; /* Number of bytes read */
581 uint32_t VolCatRecycles; /* Number of recycles this volume */
582 int32_t Slot; /* Slot in changer */
583 bool InChanger; /* Set if vol in current magazine */
584 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
585 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
586 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
587 uint64_t VolCatCapacityBytes; /* capacity estimate */
588 uint64_t VolReadTime; /* time spent reading */
589 uint64_t VolWriteTime; /* time spent writing this Volume */
590 char VolCatStatus[20]; /* Volume status */
591 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
592 };
593 struct VOLUME_CAT_INFO vol;
594
595 #ifdef xxx
596 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
597 printf("%s %d %lld\n", buf, val32, val64);
598
599 *Job=0;
600 cnt = bsscanf(catreq, Create_job_media, &Job,
601 &FirstIndex, &LastIndex, &StartFile, &EndFile,
602 &StartBlock, &EndBlock);
603 printf("cnt=%d Job=%s\n", cnt, Job);
604 cnt = bsscanf(helloreq, hello, &Job);
605 printf("cnt=%d Agent=%s\n", cnt, Job);
606 #endif
607 cnt = bsscanf(media, OK_media,
608 vol.VolCatName,
609 &vol.VolCatJobs, &vol.VolCatFiles,
610 &vol.VolCatBlocks, &vol.VolCatBytes,
611 &vol.VolCatMounts, &vol.VolCatErrors,
612 &vol.VolCatWrites, &vol.VolCatMaxBytes,
613 &vol.VolCatCapacityBytes, vol.VolCatStatus,
614 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
615 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
616 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
617
618 }
619
620 #endif
621