2 * util.c miscellaneous utility subroutines for Bacula
9 Copyright (C) 2000-2006 Kern Sibbald
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 version 2 as amended with additional clauses defined in the
14 file LICENSE in the main source directory.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 the file LICENSE for additional details.
25 #include "findlib/find.h"
28 * Various Bacula Utility subroutines
32 /* Return true of buffer has all zero bytes */
33 int is_buf_zero(char *buf, int len)
37 int i, len64, done, rem;
43 /* Optimize by checking uint64_t for zero */
44 len64 = len / sizeof(uint64_t);
45 for (i=0; i < len64; i++) {
50 done = len64 * sizeof(uint64_t); /* bytes already checked */
53 for (i = 0; i < rem; i++) {
62 /* Convert a string in place to lower case */
67 *str = tolower((int)(*str));
72 /* Convert spaces to non-space character.
73 * This makes scanf of fields containing spaces easier.
76 bash_spaces(char *str)
85 /* Convert spaces to non-space character.
86 * This makes scanf of fields containing spaces easier.
89 bash_spaces(POOL_MEM &pm)
91 char *str = pm.c_str();
100 /* Convert non-space characters (0x1) back into spaces */
102 unbash_spaces(char *str)
111 /* Convert non-space characters (0x1) back into spaces */
113 unbash_spaces(POOL_MEM &pm)
115 char *str = pm.c_str();
123 char *encode_time(time_t time, char *buf)
128 #if defined(HAVE_WIN32)
130 * Avoid a seg fault in Microsoft's CRT localtime_r(),
131 * which incorrectly references a NULL returned from gmtime() if
132 * time is negative before or after the timezone adjustment.
136 if ((gtm = gmtime(&time)) == NULL) {
140 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
145 if (localtime_r(&time, &tm)) {
146 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
147 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
148 tm.tm_hour, tm.tm_min, tm.tm_sec);
156 * Convert a JobStatus code into a human readable form
158 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
165 jobstat = _("Created");
168 jobstat = _("Running");
171 jobstat = _("Blocked");
177 case JS_ErrorTerminated:
178 jobstat = _("Error");
181 jobstat = _("Non-fatal error");
184 jobstat = _("Canceled");
187 jobstat = _("Verify differences");
190 jobstat = _("Waiting on FD");
193 jobstat = _("Wait on SD");
196 jobstat = _("Wait for new Volume");
199 jobstat = _("Waiting for mount");
201 case JS_WaitStoreRes:
202 jobstat = _("Waiting for Storage resource");
205 jobstat = _("Waiting for Job resource");
207 case JS_WaitClientRes:
208 jobstat = _("Waiting for Client resource");
211 jobstat = _("Waiting on Max Jobs");
213 case JS_WaitStartTime:
214 jobstat = _("Waiting for Start Time");
216 case JS_WaitPriority:
217 jobstat = _("Waiting on Priority");
221 if (JobStatus == 0) {
224 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
229 bstrncpy(msg, jobstat, maxlen);
233 * Convert Job Termination Status into a string
235 const char *job_status_to_str(int stat)
243 case JS_ErrorTerminated:
248 str = _("Fatal Error");
254 str = _("Differences");
257 str = _("Unknown term code");
265 * Convert Job Type into a string
267 const char *job_type_to_str(int type)
294 str = _("System or Console");
300 str = _("Unknown Type");
307 * Convert Job Level into a string
309 const char *job_level_to_str(int level)
320 str = _("Incremental");
323 str = _("Differential");
328 case L_VERIFY_CATALOG:
329 str = _("Verify Catalog");
332 str = _("Verify Init Catalog");
334 case L_VERIFY_VOLUME_TO_CATALOG:
335 str = _("Verify Volume to Catalog");
337 case L_VERIFY_DISK_TO_CATALOG:
338 str = _("Verify Disk to Catalog");
341 str = _("Verify Data");
347 str = _("Unknown Job Level");
354 /***********************************************************************
355 * Encode the mode bits into a 10 character string like LS does
356 ***********************************************************************/
358 char *encode_mode(mode_t mode, char *buf)
362 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
363 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
364 *cp++ = mode & S_IRUSR ? 'r' : '-';
365 *cp++ = mode & S_IWUSR ? 'w' : '-';
366 *cp++ = (mode & S_ISUID
367 ? (mode & S_IXUSR ? 's' : 'S')
368 : (mode & S_IXUSR ? 'x' : '-'));
369 *cp++ = mode & S_IRGRP ? 'r' : '-';
370 *cp++ = mode & S_IWGRP ? 'w' : '-';
371 *cp++ = (mode & S_ISGID
372 ? (mode & S_IXGRP ? 's' : 'S')
373 : (mode & S_IXGRP ? 'x' : '-'));
374 *cp++ = mode & S_IROTH ? 'r' : '-';
375 *cp++ = mode & S_IWOTH ? 'w' : '-';
376 *cp++ = (mode & S_ISVTX
377 ? (mode & S_IXOTH ? 't' : 'T')
378 : (mode & S_IXOTH ? 'x' : '-'));
384 int do_shell_expansion(char *name, int name_len)
386 static char meta[] = "~\\$[]*?`'<>\"";
391 char line[MAXSTRING];
392 const char *shellcmd;
394 /* Check if any meta characters are present */
396 for (i = 0; i < len; i++) {
397 if (strchr(name, meta[i])) {
403 cmd = get_pool_memory(PM_FNAME);
405 if ((shellcmd = getenv("SHELL")) == NULL) {
406 shellcmd = "/bin/sh";
408 pm_strcpy(&cmd, shellcmd);
409 pm_strcat(&cmd, " -c \"echo ");
410 pm_strcat(&cmd, name);
411 pm_strcat(&cmd, "\"");
412 Dmsg1(400, "Send: %s\n", cmd);
413 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
415 fgets(line, sizeof(line), bpipe->rfd);
416 strip_trailing_junk(line);
417 stat = close_bpipe(bpipe);
418 Dmsg2(400, "stat=%d got: %s\n", stat, line);
420 stat = 1; /* error */
422 free_pool_memory(cmd);
424 bstrncpy(name, line, name_len);
431 /* MAKESESSIONKEY -- Generate session key with optional start
432 key. If mode is TRUE, the key will be
433 translated to a string, otherwise it is
434 returned as 16 binary bytes.
436 from SpeakFreely by John Walker */
438 void make_session_key(char *key, char *seed, int mode)
441 struct MD5Context md5c;
442 unsigned char md5key[16], md5key1[16];
447 bstrncat(s, seed, sizeof(s));
450 /* The following creates a seed for the session key generator
451 based on a collection of volatile and environment-specific
452 information unlikely to be vulnerable (as a whole) to an
453 exhaustive search attack. If one of these items isn't
454 available on your machine, replace it with something
455 equivalent or, if you like, just delete it. */
457 #if defined(HAVE_WIN32)
465 sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
466 (void)getcwd(s + strlen(s), 256);
467 sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
468 QueryPerformanceCounter(&li);
469 sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
470 GetSystemTimeAsFileTime(&ft);
471 sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
472 sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
474 GetComputerName(s + strlen(s), &length);
476 GetUserName(s + strlen(s), &length);
479 sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
480 sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
481 (void)getcwd(s + strlen(s), 256);
482 sprintf(s + strlen(s), "%lu", (unsigned long)clock());
483 sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
485 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
487 #if defined(HAVE_GETHOSTID)
488 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
490 gethostname(s + strlen(s), 256);
491 sprintf(s + strlen(s), "%u", (unsigned)getuid());
492 sprintf(s + strlen(s), "%u", (unsigned)getgid());
495 MD5Update(&md5c, (unsigned char *)s, strlen(s));
496 MD5Final(md5key, &md5c);
497 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
499 MD5Update(&md5c, (unsigned char *)s, strlen(s));
500 MD5Final(md5key1, &md5c);
501 #define nextrand (md5key[j] ^ md5key1[j])
503 for (j = k = 0; j < 16; j++) {
504 unsigned char rb = nextrand;
506 #define Rad16(x) ((x) + 'A')
507 key[k++] = Rad16((rb >> 4) & 0xF);
508 key[k++] = Rad16(rb & 0xF);
516 for (j = 0; j < 16; j++) {
526 * Edit job codes into main command line
529 * %d = Director's name
534 * %n = Unadorned Job name
536 * %t = Job type (Backup, ...)
540 * omsg = edited output message
541 * imsg = input string containing edit codes (%x)
542 * to = recepients list
545 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
550 char name[MAX_NAME_LENGTH];
554 Dmsg1(200, "edit_job_codes: %s\n", imsg);
555 for (p=imsg; *p; p++) {
563 str = jcr->client_name;
569 str = my_name; /* Director's name */
573 str = job_status_to_str(jcr->JobStatus);
580 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
586 case 'j': /* Job name */
595 str = job_level_to_str(jcr->JobLevel);
602 bstrncpy(name, jcr->Job, sizeof(name));
603 /* There are three periods after the Job name */
604 for (i=0; i<3; i++) {
605 if ((q=strrchr(name, '.')) != NULL) {
617 case 's': /* since time */
618 if (jcr && jcr->stime) {
626 str = job_type_to_str(jcr->JobType);
633 if (jcr->VolumeName && jcr->VolumeName[0]) {
634 str = jcr->VolumeName;
654 Dmsg1(1200, "add_str %s\n", str);
655 pm_strcat(&omsg, str);
656 Dmsg1(1200, "omsg=%s\n", omsg);
661 void set_working_directory(char *wd)
663 struct stat stat_buf;
666 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
668 if (stat(wd, &stat_buf) != 0) {
669 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
672 if (!S_ISDIR(stat_buf.st_mode)) {
673 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
676 working_directory = wd; /* set global */