2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * scan.c -- scanning routines for Bacula
22 * Kern Sibbald, MM separated from util.c MMIII
29 #include "findlib/find.h"
31 /* Strip leading space from command line arguments */
32 void strip_leading_space(char *str)
35 while (B_ISSPACE(*p)) {
45 /* Strip any trailing junk from the command */
46 char *strip_trailing_junk(char *cmd)
50 /* strip trailing junk from command */
51 p = cmd - 1 + strlen(cmd);
52 while ((p >= cmd) && (B_ISSPACE(*p) || *p == '\n' || *p == '\r')) {
58 /* Strip any trailing newline characters from the string */
59 char *strip_trailing_newline(char *cmd)
62 p = cmd - 1 + strlen(cmd);
63 while ((p >= cmd) && (*p == '\n' || *p == '\r')) *p-- = 0;
67 /* Strip any trailing slashes from a directory path */
68 char *strip_trailing_slashes(char *dir)
72 /* strip trailing slashes */
73 p = dir -1 + strlen(dir);
74 while (p >= dir && IsPathSeparator(*p)) *p-- = 0;
80 * Returns: 0 on failure (EOF)
82 * new address in passed parameter
84 bool skip_spaces(char **msg)
90 while (*p && B_ISSPACE(*p)) {
94 return *p ? true : false;
99 * Returns: 0 on failure (EOF)
101 * new address in passed parameter
103 bool skip_nonspaces(char **msg)
110 while (*p && !B_ISSPACE(*p)) {
114 return *p ? true : false;
117 /* folded search for string - case insensitive */
119 fstrsch(const char *a, const char *b) /* folded case search */
126 while (*s1) { /* do it the fast way */
127 if ((*s1++ | 0x20) != (*s2++ | 0x20))
128 return 0; /* failed */
130 while (*a) { /* do it over the correct slow way */
131 if (B_ISUPPER(c1 = *a)) {
132 c1 = tolower((int)c1);
134 if (B_ISUPPER(c2 = *b)) {
135 c2 = tolower((int)c2);
148 * Return next argument from command line. Note, this
149 * routine is destructive because it stored 0 at the end
151 * Called with pointer to pointer to command line. This
152 * pointer is updated to point to the remainder of the
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
162 char *next_arg(char **s)
165 bool in_quote = false;
167 /* skip past spaces to next arg */
168 for (p=*s; *p && B_ISSPACE(*p); ) {
171 Dmsg1(900, "Next arg=%s\n", p);
172 for (n = q = p; *p ; ) {
173 if (*p == '\\') { /* slash? */
174 p++; /* yes, skip it */
182 if (*p == '"') { /* start or end of quote */
184 in_quote = !in_quote; /* change state */
187 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
195 Dmsg2(900, "End arg=%s next=%s\n", n, p);
200 * This routine parses the input command line.
201 * It makes a copy in args, then builds an
202 * argc, argk, argv list where:
204 * argc = count of arguments
205 * argk[i] = argument keyword (part preceding =)
206 * argv[i] = argument value (part after =)
208 * example: arg1 arg2=abc arg3=
218 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
219 char **argk, char **argv, int max_args)
223 parse_args_only(cmd, args, argc, argk, argv, max_args);
225 /* Separate keyword and value */
226 for (int i=0; i < *argc; i++) {
227 p = strchr(argk[i], '=');
229 *p++ = 0; /* terminate keyword and point to value */
231 argv[i] = p; /* save ptr to value or NULL */
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");
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.
251 * argc = count of arguments
252 * argk[i] = argument keyword (part preceding =)
255 * example: arg1 arg2=abc arg3=
265 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
266 char **argk, char **argv, int max_args)
270 pm_strcpy(args, cmd);
271 strip_trailing_junk(*args);
274 /* Pick up all arguments */
275 while (*argc < max_args) {
279 argv[(*argc)++] = NULL;
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.
293 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
294 POOLMEM **file, int *fnl)
298 int len = slen = strlen(fname);
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:).
308 /* "strip" any trailing slashes */
309 while (slen > 1 && IsPathSeparator(*f)) {
313 /* Walk back to last slash -- begin of filename */
314 while (slen > 0 && !IsPathSeparator(*f)) {
318 if (IsPathSeparator(*f)) { /* did we find a slash? */
319 f++; /* yes, point to filename */
320 } else { /* no, whole thing must be path name */
323 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
324 *fnl = fname - f + len;
326 *file = check_pool_memory_size(*file, *fnl+1);
327 memcpy(*file, f, *fnl); /* copy filename */
333 *path = check_pool_memory_size(*path, *pnl+1);
334 memcpy(*path, fname, *pnl);
338 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
339 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
343 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
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.
349 const int BIG = 1000;
350 int bsscanf(const char *buf, const char *fmt, ...)
363 while (*fmt && !error) {
364 // Dmsg1(000, "fmt=%c\n", *fmt);
367 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
372 while (B_ISDIGIT(*buf)) {
373 value = B_TIMES10(value) + *buf++ - '0';
375 vp = (void *)va_arg(ap, void *);
376 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
378 *((int *)vp) = (int)value;
380 *((uint32_t *)vp) = (uint32_t)value;
381 // Dmsg0(000, "Store 32 bit int\n");
383 *((uint64_t *)vp) = (uint64_t)value;
384 // Dmsg0(000, "Store 64 bit int\n");
397 while (B_ISDIGIT(*buf)) {
398 value = B_TIMES10(value) + *buf++ - '0';
403 vp = (void *)va_arg(ap, void *);
404 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
406 *((int *)vp) = (int)value;
408 *((int32_t *)vp) = (int32_t)value;
409 // Dmsg0(000, "Store 32 bit int\n");
411 *((int64_t *)vp) = (int64_t)value;
412 // Dmsg0(000, "Store 64 bit int\n");
418 // Dmsg0(000, "got l\n");
424 if (*fmt == 'd' || *fmt == 'u') {
427 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
432 if (*fmt == 'd' || *fmt == 'u') {
435 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
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) {
449 cp = (char *)va_arg(ap, char *);
461 while (B_ISDIGIT(*fmt)) {
462 max_len = B_TIMES10(max_len) + *fmt++ - '0';
464 // Dmsg1(000, "Default max_len=%d\n", max_len);
468 // Dmsg1(000, "Default c=%c\n", *fmt);
470 break; /* error: unknown format */
474 /* White space eats zero or more whitespace */
475 } else if (B_ISSPACE(*fmt)) {
477 while (B_ISSPACE(*buf)) {
480 /* Plain text must match */
481 } else if (*buf++ != *fmt++) {
482 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
488 // Dmsg2(000, "Error=%d count=%d\n", error, count);
496 * Return next name from a comma separated list. Note, this
497 * routine is destructive because it stored 0 at the end
499 * Called with pointer to pointer to command line. This
500 * pointer is updated to point to the remainder of the
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
510 char *next_name(char **s)
513 bool in_quote = false;
515 if (s == NULL || *s == NULL || **s == '\0') {
519 Dmsg1(900, "Next name=%s\n", p);
520 for (n = q = p; *p ; ) {
521 if (*p == '\\') { /* slash? */
522 p++; /* yes, skip it */
530 if (*p == '"') { /* start or end of quote */
532 in_quote = !in_quote; /* change state */
535 if (!in_quote && *p == ',') { /* end of field */
543 Dmsg2(900, "End arg=%s next=%s\n", n, p);
548 int main(int argc, char *argv[])
553 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
556 char *helloreq= "Hello *UserAgent* calling\n";
557 char *hello = "Hello %127s calling\n";
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;
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 */
593 struct VOLUME_CAT_INFO vol;
596 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
597 printf("%s %d %lld\n", buf, val32, val64);
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);
607 cnt = bsscanf(media, OK_media,
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);