1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2018 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * util.c miscellaneous utility subroutines for BAREOS
24 *
25 * Kern Sibbald, MM
26 */
27
28 #include "include/bareos.h"
29 #include "include/jcr.h"
30 #include "lib/edit.h"
31 #include "lib/ascii_control_characters.h"
32 #include "lib/bstringlist.h"
33 #include "lib/qualified_resource_name_type_converter.h"
34 #include "include/version_numbers.h"
35
36 #include <algorithm>
37 #include <string>
38
39 /*
40 * Various BAREOS Utility subroutines
41 */
42
43 /*
44 * Escape special characters in bareos configuration strings
45 * needed for dumping config strings
46 */
EscapeString(PoolMem & snew,const char * old,int len)47 void EscapeString(PoolMem& snew, const char* old, int len)
48 {
49 char* n;
50 const char* o;
51
52 snew.check_size(len * 2);
53 n = snew.c_str();
54 o = old;
55 while (len--) {
56 switch (*o) {
57 case '\'':
58 *n++ = '\'';
59 *n++ = '\'';
60 o++;
61 break;
62 case '\\':
63 *n++ = '\\';
64 *n++ = '\\';
65 o++;
66 break;
67 case 0:
68 *n++ = '\\';
69 *n++ = 0;
70 o++;
71 break;
72 case '(':
73 case ')':
74 case '<':
75 case '>':
76 case '"':
77 *n++ = '\\';
78 *n++ = *o++;
79 break;
80 default:
81 *n++ = *o++;
82 break;
83 }
84 }
85 *n = 0;
86 }
87
88 /*
89 * Return true of buffer has all zero bytes
90 */
IsBufZero(char * buf,int len)91 bool IsBufZero(char* buf, int len)
92 {
93 uint64_t* ip;
94 char* p;
95 int i, len64, done, rem;
96
97 if (buf[0] != 0) { return false; }
98 ip = (uint64_t*)buf;
99
100 /*
101 * Optimize by checking uint64_t for zero
102 */
103 len64 = len / sizeof(uint64_t);
104 for (i = 0; i < len64; i++) {
105 if (ip[i] != 0) { return false; }
106 }
107 done = len64 * sizeof(uint64_t); /* bytes already checked */
108 p = buf + done;
109 rem = len - done;
110 for (i = 0; i < rem; i++) {
111 if (p[i] != 0) { return false; }
112 }
113 return true;
114 }
115
116
117 /*
118 * Convert a string in place to lower case
119 */
lcase(char * str)120 void lcase(char* str)
121 {
122 while (*str) {
123 if (B_ISUPPER(*str)) { *str = tolower((int)(*str)); }
124 str++;
125 }
126 }
127
128 /*
129 * Convert spaces to non-space character.
130 * This makes scanf of fields containing spaces easier.
131 */
BashSpaces(char * str)132 void BashSpaces(char* str)
133 {
134 while (*str) {
135 if (*str == ' ') *str = 0x1;
136 str++;
137 }
138 }
139
BashSpaces(std::string & str)140 void BashSpaces(std::string& str)
141 {
142 std::replace(str.begin(), str.end(), ' ', static_cast<char>(0x1));
143 }
144
BashSpaces(PoolMem & pm)145 void BashSpaces(PoolMem& pm)
146 {
147 char* str = pm.c_str();
148 while (*str) {
149 if (*str == ' ') *str = 0x1;
150 str++;
151 }
152 }
153
154 /*
155 * Convert non-space characters (0x1) back into spaces
156 */
UnbashSpaces(char * str)157 void UnbashSpaces(char* str)
158 {
159 while (*str) {
160 if (*str == 0x1) *str = ' ';
161 str++;
162 }
163 }
164
165 /*
166 * Convert non-space characters (0x1) back into spaces
167 */
UnbashSpaces(PoolMem & pm)168 void UnbashSpaces(PoolMem& pm)
169 {
170 char* str = pm.c_str();
171 while (*str) {
172 if (*str == 0x1) *str = ' ';
173 str++;
174 }
175 }
176
177 struct HelloInformation {
178 std::string hello_string;
179 std::string resource_type_string;
180 uint32_t position_of_name;
181 int32_t position_of_version;
182 };
183
184 using SizeTypeOfHelloList = std::vector<std::string>::size_type;
185
186 static std::list<HelloInformation> hello_list{
187 /* this order is important */
188 {"Hello Storage calling Start Job", "R_JOB", 5, -1},
189 {"Hello Start Storage Job", "R_JOB", 4, -1},
190 {"Hello Start Job", "R_JOB", 3, -1},
191 {"Hello Director", "R_DIRECTOR", 2, -1},
192 {"Hello Storage", "R_STORAGE", 2, -1},
193 {"Hello Client", "R_CLIENT", 2, -1},
194 {"Hello", "R_CONSOLE", 1, 4} /* "Hello %s calling version %s" */
195 };
196
GetNameAndResourceTypeAndVersionFromHello(const std::string & input,std::string & name,std::string & r_type_str,BareosVersionNumber & bareos_version)197 bool GetNameAndResourceTypeAndVersionFromHello(
198 const std::string& input,
199 std::string& name,
200 std::string& r_type_str,
201 BareosVersionNumber& bareos_version)
202 {
203 std::list<HelloInformation>::const_iterator hello = hello_list.cbegin();
204
205 bool found = false;
206 while (hello != hello_list.cend()) {
207 uint32_t size = hello->hello_string.size();
208 uint32_t input_size = input.size();
209 if (input_size >= size) {
210 if (!input.compare(0, size, hello->hello_string)) {
211 found = true;
212 break;
213 }
214 }
215 hello++;
216 }
217
218 if (!found) {
219 Dmsg1(100, "Client information not found: %s", input.c_str());
220 return false;
221 }
222
223 BStringList arguments_of_hello_string(input, ' '); /* split at blanks */
224
225 bool ok = false;
226 if (arguments_of_hello_string.size() > hello->position_of_name) {
227 name = arguments_of_hello_string[hello->position_of_name];
228 std::replace(name.begin(), name.end(), (char)0x1, ' ');
229 r_type_str = hello->resource_type_string;
230 ok = true;
231 } else {
232 Dmsg0(100, "Failed to retrieve the name from hello message\n");
233 }
234
235 if (ok) {
236 bareos_version = BareosVersionNumber::kUndefined;
237 if (hello->position_of_version >= 0) {
238 if (arguments_of_hello_string.size() >
239 static_cast<SizeTypeOfHelloList>(hello->position_of_version)) {
240 std::string version_str =
241 arguments_of_hello_string[hello->position_of_version];
242 if (!version_str.empty()) {
243 ok = false;
244 BStringList splittet_version(version_str, '.');
245 if (splittet_version.size() > 1) {
246 uint32_t v;
247 try {
248 v = std::stoul(splittet_version[0]) * 100;
249 v += std::stoul(splittet_version[1]);
250 bareos_version = static_cast<BareosVersionNumber>(v);
251 ok = true;
252 } catch (const std::exception& e) {
253 Dmsg0(100, "Could not read out any version from hello message\n");
254 }
255 }
256 }
257 }
258 }
259 }
260
261 return ok;
262 }
263
264 /*
265 * Parameter:
266 * resultbuffer: one line string
267 * mutlilinestring: multiline string (separated by "\n")
268 * separator: separator string
269 *
270 * multilinestring should be indented according to resultbuffer.
271 *
272 * return:
273 * resultbuffer: multilinestring will be added to resultbuffer.
274 *
275 * Example:
276 * resultbuffer="initial string"
277 * mutlilinestring="line1\nline2\nline3"
278 * separator="->"
279 * Result:
280 * initial string->line1
281 * ->line2
282 * ->line3
283 */
IndentMultilineString(PoolMem & resultbuffer,const char * multilinestring,const char * separator)284 const char* IndentMultilineString(PoolMem& resultbuffer,
285 const char* multilinestring,
286 const char* separator)
287 {
288 PoolMem multiline(multilinestring);
289 PoolMem indent(PM_MESSAGE);
290 char* p1 = multiline.c_str();
291 char* p2 = NULL;
292 bool line1 = true;
293 size_t len;
294
295 /* create indentation string */
296 for (len = resultbuffer.strlen(); len > 0; len--) { indent.strcat(" "); }
297 indent.strcat(separator);
298
299 resultbuffer.strcat(separator);
300
301 while ((p2 = strchr(p1, '\n')) != NULL) {
302 *p2 = 0;
303 if (!line1) { resultbuffer.strcat(indent); }
304 resultbuffer.strcat(p1);
305 resultbuffer.strcat("\n");
306 p1 = p2 + 1;
307 line1 = false;
308 }
309
310 if (!line1) { resultbuffer.strcat(indent); }
311 resultbuffer.strcat(p1);
312
313 return resultbuffer.c_str();
314 }
315
encode_time(utime_t utime,char * buf)316 char* encode_time(utime_t utime, char* buf)
317 {
318 struct tm tm;
319 int n = 0;
320 time_t time = utime;
321
322 #if defined(HAVE_WIN32)
323 /*
324 * Avoid a seg fault in Microsoft's CRT localtime_r(),
325 * which incorrectly references a NULL returned from gmtime() if
326 * time is negative before or after the timezone adjustment.
327 */
328 struct tm* gtm;
329
330 if ((gtm = gmtime(&time)) == NULL) { return buf; }
331
332 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
333 return buf;
334 }
335 #endif
336
337 Blocaltime(&time, &tm);
338 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900,
339 tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
340
341 return buf + n;
342 }
343
ConvertTimeoutToTimespec(timespec & timeout,int timeout_in_seconds)344 bool ConvertTimeoutToTimespec(timespec& timeout, int timeout_in_seconds)
345 {
346 struct timeval tv;
347 struct timezone tz;
348
349 gettimeofday(&tv, &tz);
350 timeout.tv_nsec = tv.tv_usec * 1000;
351 timeout.tv_sec = tv.tv_sec + timeout_in_seconds;
352
353 return true;
354 }
355
356 /*
357 * Convert a JobStatus code into a human readable form
358 */
JobstatusToAscii(int JobStatus,char * msg,int maxlen)359 void JobstatusToAscii(int JobStatus, char* msg, int maxlen)
360 {
361 const char* jobstat;
362 char buf[100];
363
364 switch (JobStatus) {
365 case JS_Created:
366 jobstat = _("Created");
367 break;
368 case JS_Running:
369 jobstat = _("Running");
370 break;
371 case JS_Blocked:
372 jobstat = _("Blocked");
373 break;
374 case JS_Terminated:
375 jobstat = _("OK");
376 break;
377 case JS_Incomplete:
378 jobstat = _("Error: incomplete job");
379 break;
380 case JS_FatalError:
381 jobstat = _("Fatal Error");
382 break;
383 case JS_ErrorTerminated:
384 jobstat = _("Error");
385 break;
386 case JS_Error:
387 jobstat = _("Non-fatal error");
388 break;
389 case JS_Warnings:
390 jobstat = _("OK -- with warnings");
391 break;
392 case JS_Canceled:
393 jobstat = _("Canceled");
394 break;
395 case JS_Differences:
396 jobstat = _("Verify differences");
397 break;
398 case JS_WaitFD:
399 jobstat = _("Waiting on FD");
400 break;
401 case JS_WaitSD:
402 jobstat = _("Wait on SD");
403 break;
404 case JS_WaitMedia:
405 jobstat = _("Wait for new Volume");
406 break;
407 case JS_WaitMount:
408 jobstat = _("Waiting for mount");
409 break;
410 case JS_WaitStoreRes:
411 jobstat = _("Waiting for Storage resource");
412 break;
413 case JS_WaitJobRes:
414 jobstat = _("Waiting for Job resource");
415 break;
416 case JS_WaitClientRes:
417 jobstat = _("Waiting for Client resource");
418 break;
419 case JS_WaitMaxJobs:
420 jobstat = _("Waiting on Max Jobs");
421 break;
422 case JS_WaitStartTime:
423 jobstat = _("Waiting for Start Time");
424 break;
425 case JS_WaitPriority:
426 jobstat = _("Waiting on Priority");
427 break;
428 case JS_DataCommitting:
429 jobstat = _("SD committing Data");
430 break;
431 case JS_DataDespooling:
432 jobstat = _("SD despooling Data");
433 break;
434 case JS_AttrDespooling:
435 jobstat = _("SD despooling Attributes");
436 break;
437 case JS_AttrInserting:
438 jobstat = _("Dir inserting Attributes");
439 break;
440 default:
441 if (JobStatus == 0) {
442 buf[0] = 0;
443 } else {
444 Bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"),
445 JobStatus);
446 }
447 jobstat = buf;
448 break;
449 }
450 bstrncpy(msg, jobstat, maxlen);
451 }
452
453 /*
454 * Convert a JobStatus code into a human readable form - gui version
455 */
JobstatusToAsciiGui(int JobStatus,char * msg,int maxlen)456 void JobstatusToAsciiGui(int JobStatus, char* msg, int maxlen)
457 {
458 const char* cnv = NULL;
459 switch (JobStatus) {
460 case JS_Terminated:
461 cnv = _("Completed successfully");
462 break;
463 case JS_Warnings:
464 cnv = _("Completed with warnings");
465 break;
466 case JS_ErrorTerminated:
467 cnv = _("Terminated with errors");
468 break;
469 case JS_FatalError:
470 cnv = _("Fatal error");
471 break;
472 case JS_Created:
473 cnv = _("Created, not yet running");
474 break;
475 case JS_Canceled:
476 cnv = _("Canceled by user");
477 break;
478 case JS_Differences:
479 cnv = _("Verify found differences");
480 break;
481 case JS_WaitFD:
482 cnv = _("Waiting for File daemon");
483 break;
484 case JS_WaitSD:
485 cnv = _("Waiting for Storage daemon");
486 break;
487 case JS_WaitPriority:
488 cnv = _("Waiting for higher priority jobs");
489 break;
490 case JS_AttrInserting:
491 cnv = _("Batch inserting file records");
492 break;
493 }
494
495 if (cnv) {
496 bstrncpy(msg, cnv, maxlen);
497 } else {
498 JobstatusToAscii(JobStatus, msg, maxlen);
499 }
500 }
501
502 /*
503 * Convert Job Termination Status into a string
504 */
job_status_to_str(int stat)505 const char* job_status_to_str(int stat)
506 {
507 const char* str;
508
509 switch (stat) {
510 case JS_Terminated:
511 str = _("OK");
512 break;
513 case JS_Warnings:
514 str = _("OK -- with warnings");
515 break;
516 case JS_ErrorTerminated:
517 case JS_Error:
518 str = _("Error");
519 break;
520 case JS_FatalError:
521 str = _("Fatal Error");
522 break;
523 case JS_Canceled:
524 str = _("Canceled");
525 break;
526 case JS_Differences:
527 str = _("Differences");
528 break;
529 default:
530 str = _("Unknown term code");
531 break;
532 }
533 return str;
534 }
535
536 /*
537 * Convert Job Type into a string
538 */
job_type_to_str(int type)539 const char* job_type_to_str(int type)
540 {
541 const char* str = NULL;
542
543 switch (type) {
544 case JT_BACKUP:
545 str = _("Backup");
546 break;
547 case JT_MIGRATED_JOB:
548 str = _("Migrated Job");
549 break;
550 case JT_VERIFY:
551 str = _("Verify");
552 break;
553 case JT_RESTORE:
554 str = _("Restore");
555 break;
556 case JT_CONSOLE:
557 str = _("Console");
558 break;
559 case JT_SYSTEM:
560 str = _("System or Console");
561 break;
562 case JT_ADMIN:
563 str = _("Admin");
564 break;
565 case JT_ARCHIVE:
566 str = _("Archive");
567 break;
568 case JT_JOB_COPY:
569 str = _("Job Copy");
570 break;
571 case JT_COPY:
572 str = _("Copy");
573 break;
574 case JT_MIGRATE:
575 str = _("Migrate");
576 break;
577 case JT_SCAN:
578 str = _("Scan");
579 break;
580 case JT_CONSOLIDATE:
581 str = _("Consolidate");
582 break;
583 }
584 if (!str) { str = _("Unknown Type"); }
585 return str;
586 }
587
588 /*
589 * Convert ActionOnPurge to string (Truncate, Erase, Destroy)
590 */
action_on_purge_to_string(int aop,PoolMem & ret)591 char* action_on_purge_to_string(int aop, PoolMem& ret)
592 {
593 if (aop & ON_PURGE_TRUNCATE) { PmStrcpy(ret, _("Truncate")); }
594 if (!aop) { PmStrcpy(ret, _("None")); }
595 return ret.c_str();
596 }
597
598 /*
599 * Convert Job Level into a string
600 */
job_level_to_str(int level)601 const char* job_level_to_str(int level)
602 {
603 const char* str;
604
605 switch (level) {
606 case L_BASE:
607 str = _("Base");
608 break;
609 case L_FULL:
610 str = _("Full");
611 break;
612 case L_INCREMENTAL:
613 str = _("Incremental");
614 break;
615 case L_DIFFERENTIAL:
616 str = _("Differential");
617 break;
618 case L_SINCE:
619 str = _("Since");
620 break;
621 case L_VERIFY_CATALOG:
622 str = _("Verify Catalog");
623 break;
624 case L_VERIFY_INIT:
625 str = _("Verify Init Catalog");
626 break;
627 case L_VERIFY_VOLUME_TO_CATALOG:
628 str = _("Verify Volume to Catalog");
629 break;
630 case L_VERIFY_DISK_TO_CATALOG:
631 str = _("Verify Disk to Catalog");
632 break;
633 case L_VERIFY_DATA:
634 str = _("Verify Data");
635 break;
636 case L_VIRTUAL_FULL:
637 str = _("Virtual Full");
638 break;
639 case L_NONE:
640 str = " ";
641 break;
642 default:
643 str = _("Unknown Job Level");
644 break;
645 }
646 return str;
647 }
648
volume_status_to_str(const char * status)649 const char* volume_status_to_str(const char* status)
650 {
651 int pos;
652 const char* vs[] = {NT_("Append"), _("Append"), NT_("Archive"),
653 _("Archive"), NT_("Disabled"), _("Disabled"),
654 NT_("Full"), _("Full"), NT_("Used"),
655 _("Used"), NT_("Cleaning"), _("Cleaning"),
656 NT_("Purged"), _("Purged"), NT_("Recycle"),
657 _("Recycle"), NT_("Read-Only"), _("Read-Only"),
658 NT_("Error"), _("Error"), NULL,
659 NULL};
660
661 if (status) {
662 for (pos = 0; vs[pos]; pos += 2) {
663 if (bstrcmp(vs[pos], status)) { return vs[pos + 1]; }
664 }
665 }
666
667 return _("Invalid volume status");
668 }
669
670
671 /*
672 * Encode the mode bits into a 10 character string like LS does
673 */
encode_mode(mode_t mode,char * buf)674 char* encode_mode(mode_t mode, char* buf)
675 {
676 char* cp = buf;
677
678 *cp++ =
679 S_ISDIR(mode)
680 ? 'd'
681 : S_ISBLK(mode)
682 ? 'b'
683 : S_ISCHR(mode)
684 ? 'c'
685 : S_ISLNK(mode)
686 ? 'l'
687 : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
688 *cp++ = mode & S_IRUSR ? 'r' : '-';
689 *cp++ = mode & S_IWUSR ? 'w' : '-';
690 *cp++ = (mode & S_ISUID ? (mode & S_IXUSR ? 's' : 'S')
691 : (mode & S_IXUSR ? 'x' : '-'));
692 *cp++ = mode & S_IRGRP ? 'r' : '-';
693 *cp++ = mode & S_IWGRP ? 'w' : '-';
694 *cp++ = (mode & S_ISGID ? (mode & S_IXGRP ? 's' : 'S')
695 : (mode & S_IXGRP ? 'x' : '-'));
696 *cp++ = mode & S_IROTH ? 'r' : '-';
697 *cp++ = mode & S_IWOTH ? 'w' : '-';
698 *cp++ = (mode & S_ISVTX ? (mode & S_IXOTH ? 't' : 'T')
699 : (mode & S_IXOTH ? 'x' : '-'));
700 *cp = '\0';
701 return cp;
702 }
703
704 #if defined(HAVE_WIN32)
DoShellExpansion(char * name,int name_len)705 int DoShellExpansion(char* name, int name_len)
706 {
707 char* src = strdup(name);
708
709 ExpandEnvironmentStrings(src, name, name_len);
710
711 free(src);
712
713 return 1;
714 }
715 #else
DoShellExpansion(char * name,int name_len)716 int DoShellExpansion(char* name, int name_len)
717 {
718 static char meta[] = "~\\$[]*?`'<>\"";
719 bool found = false;
720 int len, i, status;
721 POOLMEM *cmd, *line;
722 Bpipe* bpipe;
723 const char* shellcmd;
724
725 /*
726 * Check if any meta characters are present
727 */
728 len = strlen(meta);
729 for (i = 0; i < len; i++) {
730 if (strchr(name, meta[i])) {
731 found = true;
732 break;
733 }
734 }
735 if (found) {
736 cmd = GetPoolMemory(PM_FNAME);
737 line = GetPoolMemory(PM_FNAME);
738 /*
739 * Look for shell
740 */
741 if ((shellcmd = getenv("SHELL")) == NULL) { shellcmd = "/bin/sh"; }
742 PmStrcpy(cmd, shellcmd);
743 PmStrcat(cmd, " -c \"echo ");
744 PmStrcat(cmd, name);
745 PmStrcat(cmd, "\"");
746 Dmsg1(400, "Send: %s\n", cmd);
747 if ((bpipe = OpenBpipe(cmd, 0, "r"))) {
748 bfgets(line, bpipe->rfd);
749 StripTrailingJunk(line);
750 status = CloseBpipe(bpipe);
751 Dmsg2(400, "status=%d got: %s\n", status, line);
752 } else {
753 status = 1; /* error */
754 }
755 FreePoolMemory(cmd);
756 FreePoolMemory(line);
757 if (status == 0) { bstrncpy(name, line, name_len); }
758 }
759 return 1;
760 }
761 #endif
762
763 /*
764 * MAKESESSIONKEY -- Generate session key with optional start
765 * key. If mode is TRUE, the key will be
766 * translated to a string, otherwise it is
767 * returned as 16 binary bytes.
768 *
769 * from SpeakFreely by John Walker
770 */
MakeSessionKey(char * key,char * seed,int mode)771 void MakeSessionKey(char* key, char* seed, int mode)
772 {
773 int j, k;
774 MD5_CTX md5c;
775 unsigned char md5key[16], md5key1[16];
776 char s[1024];
777
778 #define ss sizeof(s)
779
780 s[0] = 0;
781 if (seed != NULL) { bstrncat(s, seed, sizeof(s)); }
782
783 /*
784 * The following creates a seed for the session key generator
785 * based on a collection of volatile and environment-specific
786 * information unlikely to be vulnerable (as a whole) to an
787 * exhaustive search attack. If one of these items isn't
788 * available on your machine, replace it with something
789 * equivalent or, if you like, just delete it.
790 */
791 #if defined(HAVE_WIN32)
792 {
793 LARGE_INTEGER li;
794 DWORD length;
795 FILETIME ft;
796
797 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
798 (void)getcwd(s + strlen(s), 256);
799 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
800 QueryPerformanceCounter(&li);
801 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
802 GetSystemTimeAsFileTime(&ft);
803 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
804 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
805 length = 256;
806 GetComputerName(s + strlen(s), &length);
807 length = 256;
808 GetUserName(s + strlen(s), &length);
809 }
810 #else
811 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
812 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
813 (void)getcwd(s + strlen(s), 256);
814 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
815 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
816 #if defined(Solaris)
817 sysinfo(SI_HW_SERIAL, s + strlen(s), 12);
818 #endif
819 #if defined(HAVE_GETHOSTID)
820 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)gethostid());
821 #endif
822 gethostname(s + strlen(s), 256);
823 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
824 Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
825 #endif
826 MD5_Init(&md5c);
827 MD5_Update(&md5c, (uint8_t*)s, strlen(s));
828 MD5_Final(md5key, &md5c);
829 Bsnprintf(s + strlen(s), ss, "%lu",
830 (uint32_t)((time(NULL) + 65121) ^ 0x375F));
831 MD5_Init(&md5c);
832 MD5_Update(&md5c, (uint8_t*)s, strlen(s));
833 MD5_Final(md5key1, &md5c);
834 #define nextrand (md5key[j] ^ md5key1[j])
835 if (mode) {
836 for (j = k = 0; j < 16; j++) {
837 unsigned char rb = nextrand;
838
839 #define Rad16(x) ((x) + 'A')
840 key[k++] = Rad16((rb >> 4) & 0xF);
841 key[k++] = Rad16(rb & 0xF);
842 #undef Rad16
843 if (j & 1) { key[k++] = '-'; }
844 }
845 key[--k] = 0;
846 } else {
847 for (j = 0; j < 16; j++) { key[j] = nextrand; }
848 }
849 }
850 #undef nextrand
851
EncodeSessionKey(char * encode,char * session,char * key,int maxlen)852 void EncodeSessionKey(char* encode, char* session, char* key, int maxlen)
853 {
854 int i;
855
856 for (i = 0; (i < maxlen - 1) && session[i]; i++) {
857 if (session[i] == '-') {
858 encode[i] = '-';
859 } else {
860 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
861 }
862 }
863 encode[i] = 0;
864 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
865 }
866
DecodeSessionKey(char * decode,char * session,char * key,int maxlen)867 void DecodeSessionKey(char* decode, char* session, char* key, int maxlen)
868 {
869 int i, x;
870
871 for (i = 0; (i < maxlen - 1) && session[i]; i++) {
872 if (session[i] == '-') {
873 decode[i] = '-';
874 } else {
875 x = (session[i] - 'A' - key[i]) & 0xF;
876 if (x < 0) { x += 16; }
877 decode[i] = x + 'A';
878 }
879 }
880 decode[i] = 0;
881 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
882 }
883
884 /*
885 * Edit job codes into main command line
886 * %% = %
887 * %B = Job Bytes in human readable format
888 * %F = Job Files
889 * %P = Pid of daemon
890 * %b = Job Bytes
891 * %c = Client's name
892 * %d = Director's name
893 * %e = Job Exit code
894 * %i = JobId
895 * %j = Unique Job id
896 * %l = job level
897 * %n = Unadorned Job name
898 * %r = Recipients
899 * %s = Since time
900 * %t = Job type (Backup, ...)
901 * %v = Volume name(s)
902 *
903 * omsg = edited output message
904 * imsg = input string containing edit codes (%x)
905 * to = recipients list
906 */
edit_job_codes(JobControlRecord * jcr,char * omsg,const char * imsg,const char * to,job_code_callback_t callback)907 POOLMEM* edit_job_codes(JobControlRecord* jcr,
908 char* omsg,
909 const char* imsg,
910 const char* to,
911 job_code_callback_t callback)
912 {
913 const char* p;
914 char* q;
915 const char* str;
916 char ed1[50];
917 char add[50];
918 char name[MAX_NAME_LENGTH];
919 int i;
920
921 *omsg = 0;
922 Dmsg1(200, "edit_job_codes: %s\n", imsg);
923 for (p = imsg; *p; p++) {
924 if (*p == '%') {
925 switch (*++p) {
926 case '%':
927 str = "%";
928 break;
929 case 'B': /* Job Bytes in human readable format */
930 if (jcr) {
931 Bsnprintf(add, sizeof(add), "%sB",
932 edit_uint64_with_suffix(jcr->JobBytes, ed1));
933 str = add;
934 } else {
935 str = _("*None*");
936 }
937 break;
938 case 'F': /* Job Files */
939 if (jcr) {
940 str = edit_uint64(jcr->JobFiles, add);
941 } else {
942 str = _("*None*");
943 }
944 break;
945 case 'P': /* Process Id */
946 Bsnprintf(add, sizeof(add), "%lu", (uint32_t)getpid());
947 str = add;
948 break;
949 case 'b': /* Job Bytes */
950 if (jcr) {
951 str = edit_uint64(jcr->JobBytes, add);
952 } else {
953 str = _("*None*");
954 }
955 break;
956 case 'c': /* Client's name */
957 if (jcr && jcr->client_name) {
958 str = jcr->client_name;
959 } else {
960 str = _("*None*");
961 }
962 break;
963 case 'd': /* Director's name */
964 str = my_name;
965 break;
966 case 'e': /* Job Exit code */
967 if (jcr) {
968 str = job_status_to_str(jcr->JobStatus);
969 } else {
970 str = _("*None*");
971 }
972 break;
973 case 'i': /* JobId */
974 if (jcr) {
975 Bsnprintf(add, sizeof(add), "%d", jcr->JobId);
976 str = add;
977 } else {
978 str = _("*None*");
979 }
980 break;
981 case 'j': /* Job name */
982 if (jcr) {
983 str = jcr->Job;
984 } else {
985 str = _("*None*");
986 }
987 break;
988 case 'l': /* Job level */
989 if (jcr) {
990 str = job_level_to_str(jcr->getJobLevel());
991 } else {
992 str = _("*None*");
993 }
994 break;
995 case 'n': /* Unadorned Job name */
996 if (jcr) {
997 bstrncpy(name, jcr->Job, sizeof(name));
998 /*
999 * There are three periods after the Job name
1000 */
1001 for (i = 0; i < 3; i++) {
1002 if ((q = strrchr(name, '.')) != NULL) { *q = 0; }
1003 }
1004 str = name;
1005 } else {
1006 str = _("*None*");
1007 }
1008 break;
1009 case 'r': /* Recipients */
1010 str = to;
1011 break;
1012 case 's': /* Since time */
1013 if (jcr && jcr->stime) {
1014 str = jcr->stime;
1015 } else {
1016 str = _("*None*");
1017 }
1018 break;
1019 case 't': /* Job type */
1020 if (jcr) {
1021 str = job_type_to_str(jcr->getJobType());
1022 } else {
1023 str = _("*None*");
1024 }
1025 break;
1026 case 'v': /* Volume name(s) */
1027 if (jcr) {
1028 if (jcr->VolumeName) {
1029 str = jcr->VolumeName;
1030 } else {
1031 str = _("*None*");
1032 }
1033 } else {
1034 str = _("*None*");
1035 }
1036 break;
1037 default:
1038 str = NULL;
1039 if (callback != NULL) { str = callback(jcr, p); }
1040
1041 if (!str) {
1042 add[0] = '%';
1043 add[1] = *p;
1044 add[2] = 0;
1045 str = add;
1046 }
1047 break;
1048 }
1049 } else {
1050 add[0] = *p;
1051 add[1] = 0;
1052 str = add;
1053 }
1054 Dmsg1(1200, "add_str %s\n", str);
1055 PmStrcat(omsg, str);
1056 Dmsg1(1200, "omsg=%s\n", omsg);
1057 }
1058
1059 return omsg;
1060 }
1061
SetWorkingDirectory(const char * wd)1062 void SetWorkingDirectory(const char* wd)
1063 {
1064 struct stat stat_buf;
1065
1066 if (wd == NULL) {
1067 Emsg0(M_ERROR_TERM, 0,
1068 _("Working directory not defined. Cannot continue.\n"));
1069 }
1070 if (stat(wd, &stat_buf) != 0) {
1071 Emsg1(M_ERROR_TERM, 0,
1072 _("Working Directory: \"%s\" not found. Cannot continue.\n"), wd);
1073 }
1074 if (!S_ISDIR(stat_buf.st_mode)) {
1075 Emsg1(M_ERROR_TERM, 0,
1076 _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
1077 wd);
1078 }
1079 working_directory = wd; /* set global */
1080 }
1081
last_path_separator(const char * str)1082 const char* last_path_separator(const char* str)
1083 {
1084 if (*str != '\0') {
1085 for (const char* p = &str[strlen(str) - 1]; p >= str; p--) {
1086 if (IsPathSeparator(*p)) { return p; }
1087 }
1088 }
1089 return NULL;
1090 }
1091
StringToLowerCase(std::string & s)1092 void StringToLowerCase(std::string& s)
1093 {
1094 for (auto& c : s) { c = std::tolower(c); }
1095 }
1096
StringToLowerCase(std::string & out,const std::string & in)1097 void StringToLowerCase(std::string& out, const std::string& in)
1098 {
1099 out.clear();
1100 for (const auto& c : in) { out += std::tolower(c); }
1101 }
1102
SortCaseInsensitive(std::vector<std::string> & v)1103 void SortCaseInsensitive(std::vector<std::string>& v)
1104 {
1105 if (v.empty()) { return; }
1106
1107 std::sort(v.begin(), v.end(), [](const std::string& a, const std::string& b) {
1108 std::string x{a}, y{b};
1109 StringToLowerCase(x);
1110 StringToLowerCase(y);
1111 return x < y;
1112 });
1113 }
1114
getenv_std_string(std::string env_var)1115 std::string getenv_std_string(std::string env_var)
1116 {
1117 const char* v = (std::getenv(env_var.c_str()));
1118 return v ? std::string(v) : std::string();
1119 }
1120