2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 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 int main(int argc, char *argv[])
501 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
504 char *helloreq= "Hello *UserAgent* calling\n";
505 char *hello = "Hello %127s calling\n";
507 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
508 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
509 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
510 "StartBlock=%u EndBlock=%u\n";
511 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
512 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
513 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
514 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
515 " VolReadTime=%" lld " VolWriteTime=%" lld;
517 "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";
518 struct VOLUME_CAT_INFO {
519 /* Media info for the current Volume */
520 uint32_t VolCatJobs; /* number of jobs on this Volume */
521 uint32_t VolCatFiles; /* Number of files */
522 uint32_t VolCatBlocks; /* Number of blocks */
523 uint64_t VolCatBytes; /* Number of bytes written */
524 uint32_t VolCatMounts; /* Number of mounts this volume */
525 uint32_t VolCatErrors; /* Number of errors this volume */
526 uint32_t VolCatWrites; /* Number of writes this volume */
527 uint32_t VolCatReads; /* Number of reads this volume */
528 uint64_t VolCatRBytes; /* Number of bytes read */
529 uint32_t VolCatRecycles; /* Number of recycles this volume */
530 int32_t Slot; /* Slot in changer */
531 bool InChanger; /* Set if vol in current magazine */
532 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
533 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
534 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
535 uint64_t VolCatCapacityBytes; /* capacity estimate */
536 uint64_t VolReadTime; /* time spent reading */
537 uint64_t VolWriteTime; /* time spent writing this Volume */
538 char VolCatStatus[20]; /* Volume status */
539 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
541 struct VOLUME_CAT_INFO vol;
544 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
545 printf("%s %d %lld\n", buf, val32, val64);
548 cnt = bsscanf(catreq, Create_job_media, &Job,
549 &FirstIndex, &LastIndex, &StartFile, &EndFile,
550 &StartBlock, &EndBlock);
551 printf("cnt=%d Job=%s\n", cnt, Job);
552 cnt = bsscanf(helloreq, hello, &Job);
553 printf("cnt=%d Agent=%s\n", cnt, Job);
555 cnt = bsscanf(media, OK_media,
557 &vol.VolCatJobs, &vol.VolCatFiles,
558 &vol.VolCatBlocks, &vol.VolCatBytes,
559 &vol.VolCatMounts, &vol.VolCatErrors,
560 &vol.VolCatWrites, &vol.VolCatMaxBytes,
561 &vol.VolCatCapacityBytes, vol.VolCatStatus,
562 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
563 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
564 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);