2 * util.c miscellaneous utility subroutines for Bacula
10 Copyright (C) 2000-2004 Kern Sibbald and John Walker
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation; either version 2 of
15 the License, or (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public
23 License along with this program; if not, write to the Free
24 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
31 #include "findlib/find.h"
34 * Various Bacula Utility subroutines
38 /* Return true of buffer has all zero bytes */
39 int is_buf_zero(char *buf, int len)
43 int i, len64, done, rem;
49 /* Optimize by checking uint64_t for zero */
50 len64 = len / sizeof(uint64_t);
51 for (i=0; i < len64; i++) {
56 done = len64 * sizeof(uint64_t); /* bytes already checked */
59 for (i = 0; i < rem; i++) {
68 /* Convert a string in place to lower case */
73 *str = tolower((int)(*str));
78 /* Convert spaces to non-space character.
79 * This makes scanf of fields containing spaces easier.
82 bash_spaces(char *str)
91 /* Convert spaces to non-space character.
92 * This makes scanf of fields containing spaces easier.
95 bash_spaces(POOL_MEM &pm)
97 char *str = pm.c_str();
106 /* Convert non-space characters (0x1) back into spaces */
108 unbash_spaces(char *str)
117 /* Convert non-space characters (0x1) back into spaces */
119 unbash_spaces(POOL_MEM &pm)
121 char *str = pm.c_str();
129 #if HAVE_WIN32 && !HAVE_CONSOLE && !HAVE_WXCONSOLE
130 extern long _timezone;
131 extern int _daylight;
132 extern long _dstbias;
133 extern "C" void __tzset(void);
134 extern "C" int _isindst(struct tm *);
137 char *encode_time(time_t time, char *buf)
142 #if HAVE_WIN32 && !HAVE_CONSOLE && !HAVE_WXCONSOLE
144 * Gross kludge to avoid a seg fault in Microsoft's CRT localtime_r(),
145 * which incorrectly references a NULL returned from gmtime() if
146 * the time (adjusted for the current timezone) is invalid.
147 * This could happen if you have a bad date/time, or perhaps if you
148 * moved a file from one timezone to another?
153 gtime = time - _timezone;
154 if (!(gtm = gmtime(>ime))) {
157 if (_daylight && _isindst(gtm)) {
159 if (!gmtime(>ime)) {
164 if (localtime_r(&time, &tm)) {
165 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
166 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
167 tm.tm_hour, tm.tm_min, tm.tm_sec);
175 * Convert a JobStatus code into a human readable form
177 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
184 jobstat = _("Created");
187 jobstat = _("Running");
190 jobstat = _("Blocked");
196 case JS_ErrorTerminated:
197 jobstat = _("Error");
200 jobstat = _("Non-fatal error");
203 jobstat = _("Canceled");
206 jobstat = _("Verify differences");
209 jobstat = _("Waiting on FD");
212 jobstat = _("Wait on SD");
215 jobstat = _("Wait for new Volume");
218 jobstat = _("Waiting for mount");
220 case JS_WaitStoreRes:
221 jobstat = _("Waiting for Storage resource");
224 jobstat = _("Waiting for Job resource");
226 case JS_WaitClientRes:
227 jobstat = _("Waiting for Client resource");
230 jobstat = _("Waiting on Max Jobs");
232 case JS_WaitStartTime:
233 jobstat = _("Waiting for Start Time");
235 case JS_WaitPriority:
236 jobstat = _("Waiting on Priority");
240 if (JobStatus == 0) {
243 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
248 bstrncpy(msg, jobstat, maxlen);
252 * Convert Job Termination Status into a string
254 const char *job_status_to_str(int stat)
262 case JS_ErrorTerminated:
267 str = _("Fatal Error");
273 str = _("Differences");
276 str = _("Unknown term code");
284 * Convert Job Type into a string
286 const char *job_type_to_str(int type)
304 str = _("Unknown Type");
311 * Convert Job Level into a string
313 const char *job_level_to_str(int level)
324 str = _("Incremental");
327 str = _("Differential");
332 case L_VERIFY_CATALOG:
333 str = _("Verify Catalog");
336 str = _("Verify Init Catalog");
338 case L_VERIFY_VOLUME_TO_CATALOG:
339 str = _("Verify Volume to Catalog");
341 case L_VERIFY_DISK_TO_CATALOG:
342 str = _("Verify Disk to Catalog");
345 str = _("Verify Data");
351 str = _("Unknown Job Level");
358 /***********************************************************************
359 * Encode the mode bits into a 10 character string like LS does
360 ***********************************************************************/
362 char *encode_mode(mode_t mode, char *buf)
366 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
367 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
368 *cp++ = mode & S_IRUSR ? 'r' : '-';
369 *cp++ = mode & S_IWUSR ? 'w' : '-';
370 *cp++ = (mode & S_ISUID
371 ? (mode & S_IXUSR ? 's' : 'S')
372 : (mode & S_IXUSR ? 'x' : '-'));
373 *cp++ = mode & S_IRGRP ? 'r' : '-';
374 *cp++ = mode & S_IWGRP ? 'w' : '-';
375 *cp++ = (mode & S_ISGID
376 ? (mode & S_IXGRP ? 's' : 'S')
377 : (mode & S_IXGRP ? 'x' : '-'));
378 *cp++ = mode & S_IROTH ? 'r' : '-';
379 *cp++ = mode & S_IWOTH ? 'w' : '-';
380 *cp++ = (mode & S_ISVTX
381 ? (mode & S_IXOTH ? 't' : 'T')
382 : (mode & S_IXOTH ? 'x' : '-'));
388 int do_shell_expansion(char *name, int name_len)
390 static char meta[] = "~\\$[]*?`'<>\"";
395 char line[MAXSTRING];
396 const char *shellcmd;
398 /* Check if any meta characters are present */
400 for (i = 0; i < len; i++) {
401 if (strchr(name, meta[i])) {
407 cmd = get_pool_memory(PM_FNAME);
409 if ((shellcmd = getenv("SHELL")) == NULL) {
410 shellcmd = "/bin/sh";
412 pm_strcpy(&cmd, shellcmd);
413 pm_strcat(&cmd, " -c \"echo ");
414 pm_strcat(&cmd, name);
415 pm_strcat(&cmd, "\"");
416 Dmsg1(400, "Send: %s\n", cmd);
417 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
419 fgets(line, sizeof(line), bpipe->rfd);
420 strip_trailing_junk(line);
421 stat = close_bpipe(bpipe);
422 Dmsg2(400, "stat=%d got: %s\n", stat, line);
424 stat = 1; /* error */
426 free_pool_memory(cmd);
428 bstrncpy(name, line, name_len);
435 /* MAKESESSIONKEY -- Generate session key with optional start
436 key. If mode is TRUE, the key will be
437 translated to a string, otherwise it is
438 returned as 16 binary bytes.
440 from SpeakFreely by John Walker */
442 void make_session_key(char *key, char *seed, int mode)
445 struct MD5Context md5c;
446 unsigned char md5key[16], md5key1[16];
451 bstrncat(s, seed, sizeof(s));
454 /* The following creates a seed for the session key generator
455 based on a collection of volatile and environment-specific
456 information unlikely to be vulnerable (as a whole) to an
457 exhaustive search attack. If one of these items isn't
458 available on your machine, replace it with something
459 equivalent or, if you like, just delete it. */
461 sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
462 sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
463 getcwd(s + strlen(s), 256);
464 sprintf(s + strlen(s), "%lu", (unsigned long)clock());
465 sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
467 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
469 #ifdef HAVE_GETHOSTID
470 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
472 #ifdef HAVE_GETDOMAINNAME
473 getdomainname(s + strlen(s), 256);
475 gethostname(s + strlen(s), 256);
476 sprintf(s + strlen(s), "%u", (unsigned)getuid());
477 sprintf(s + strlen(s), "%u", (unsigned)getgid());
479 MD5Update(&md5c, (unsigned char *)s, strlen(s));
480 MD5Final(md5key, &md5c);
481 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
483 MD5Update(&md5c, (unsigned char *)s, strlen(s));
484 MD5Final(md5key1, &md5c);
485 #define nextrand (md5key[j] ^ md5key1[j])
487 for (j = k = 0; j < 16; j++) {
488 unsigned char rb = nextrand;
490 #define Rad16(x) ((x) + 'A')
491 key[k++] = Rad16((rb >> 4) & 0xF);
492 key[k++] = Rad16(rb & 0xF);
500 for (j = 0; j < 16; j++) {
510 * Edit job codes into main command line
513 * %d = Director's name
516 * %j = Unique Job name
518 * %n = Unadorned Job name
520 * %t = Job type (Backup, ...)
524 * omsg = edited output message
525 * imsg = input string containing edit codes (%x)
526 * to = recepients list
529 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
534 char name[MAX_NAME_LENGTH];
538 Dmsg1(200, "edit_job_codes: %s\n", imsg);
539 for (p=imsg; *p; p++) {
547 str = jcr->client_name;
553 str = my_name; /* Director's name */
557 str = job_status_to_str(jcr->JobStatus);
564 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
570 case 'j': /* Job name */
579 str = job_level_to_str(jcr->JobLevel);
586 bstrncpy(name, jcr->Job, sizeof(name));
587 /* There are three periods after the Job name */
588 for (i=0; i<3; i++) {
589 if ((q=strrchr(name, '.')) != NULL) {
601 case 's': /* since time */
602 if (jcr && jcr->stime) {
610 str = job_type_to_str(jcr->JobType);
617 if (jcr->VolumeName && jcr->VolumeName[0]) {
618 str = jcr->VolumeName;
638 Dmsg1(1200, "add_str %s\n", str);
639 pm_strcat(&omsg, str);
640 Dmsg1(1200, "omsg=%s\n", omsg);
645 void set_working_directory(char *wd)
647 struct stat stat_buf;
650 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
652 if (stat(wd, &stat_buf) != 0) {
653 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
656 if (!S_ISDIR(stat_buf.st_mode)) {
657 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
660 working_directory = wd; /* set global */