kind.ag 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. #include <dirent.h>
  2. #include <sys/stat.h>
  3. #include <cstring>
  4. #include <unistd.h>
  5. #include <iostream>
  6. #include <fstream>
  7. #include <string>
  8. #include <vector>
  9. #include <set>
  10. #include <algorithm>
  11. #include "stringtools.h"
  12. #include "Exception.h"
  13. #include "DateTime.h"
  14. #include "FileName.h"
  15. #include "KindConfig.h"
  16. #include "filetools.h"
  17. #include "Lexer.h"
  18. #include "rulecomp.h"
  19. #include "Strings.h"
  20. /*AppGen
  21. %% Beschreibung des Programmes:
  22. prog: archiving backup
  23. %% Beschreibung Parameter
  24. % symbolischerName, Art, Typ, Variablenname, Erklärung, Default-Wert
  25. para: vault_or_group, required, string, vault, Vault to backup
  26. %% Beschreibung der Optionen
  27. % kurz-Option, lang-Option, Typ, Variablenname, Erklärung, Default-Wert
  28. opt: f, full, void, fullImage, Force full image == initial backup, false
  29. opt: c, masterconfig, string, masterConfig, Master config file, ""
  30. opt2: if not given or empty kind looks for
  31. opt2: /etc/kind/master.conf
  32. opt2: /ffp/etc/kind/master.conf
  33. opt: B, backuponly, void, backupOnly, Only backup/no expire, false
  34. opt: E, expireonly, void, expireOnly, Only expire/no backup, false
  35. opt: D, dryrun, Void, dryRun, Dry run (no real backup), false
  36. opt: v, verbose, Void, verbose, Verbose, false
  37. opt: d, debug, Void, debug, Debug output of many data, false
  38. opt: q, quiet, Void, quiet, Be quiet - no messages, false
  39. opt: h, help, usage, ignored , This help
  40. AppGen*/
  41. using namespace std;
  42. /*AppGen:Global*/
  43. vector<string> banks;
  44. typedef pair<long int, long int> Sizes;
  45. map<string, Sizes> sizes;
  46. void verbosePrint(const string& text)
  47. {
  48. if (verbose)
  49. cout << " " << text << endl;
  50. }
  51. void debugPrint(const string& text)
  52. {
  53. if (verbose)
  54. cout << " " << text << endl;
  55. }
  56. void readMasterConfig(const string& fn, KindConfig& conf)
  57. {
  58. verbosePrint("reading global config " + fn);
  59. conf.addFile(fn);
  60. banks = conf.getStrings("bank");
  61. if (banks.empty())
  62. throw Exception("read main config", "no banks defined");
  63. }
  64. string findVault(const string& v)
  65. {
  66. bool found = false;
  67. FileName fn;
  68. fn.setName(v);
  69. for (unsigned int i = 0; !found && i < banks.size(); ++i)
  70. {
  71. fn.setPath(banks[i]);
  72. if (dirExists(fn.getFileName()))
  73. found = true;
  74. }
  75. if (!found)
  76. throw Exception("find vault", v + " not found");
  77. verbosePrint("using vault " + fn.getFileName());
  78. return fn.getFileName();
  79. }
  80. void readVaultConfig(const string& vaultConfigName, KindConfig& conf)
  81. {
  82. FileName fn(vaultConfigName);
  83. verbosePrint("reading vault config " + fn.getFileName());
  84. conf.addFile(fn.getFileName());
  85. }
  86. string getImageName(const KindConfig& conf)
  87. {
  88. bool nonPortable = false;
  89. string res = conf.getString("imageName");
  90. for (unsigned int i = 0; !nonPortable && i < res.size(); ++i)
  91. {
  92. char c = res[i];
  93. if (!isalnum(c) && c != '.' && c != '_')
  94. nonPortable = true;
  95. }
  96. if (nonPortable)
  97. throw Exception("getImageName", "Invalid character in image name " + res);
  98. return res;
  99. }
  100. bool isValidImage(const string& imageName)
  101. {
  102. return dirExists(imageName) &&
  103. !fileExists(imageName + "/error") &&
  104. dirExists(imageName + "/tree");
  105. }
  106. Strings findValidImages(const string& vaultpath, const KindConfig& conf)
  107. {
  108. Strings imageList;
  109. debugPrint("searching images in " + vaultpath);
  110. dirList(vaultpath, imageList);
  111. Strings validImageList;
  112. for (unsigned int i = 0; i < imageList.size(); ++i)
  113. {
  114. FileName fn(imageList[i]);
  115. string imgname = getImageName(conf);
  116. int len = imgname.length();
  117. if (fn.getName().substr(0, len) == imgname)
  118. {
  119. debugPrint("Checking " + imageList[i]);
  120. if (isValidImage(imageList[i]))
  121. validImageList.push_back(imageList[i]);
  122. }
  123. }
  124. if (validImageList.empty())
  125. throw Exception("Find reference", "No reference found");
  126. sort(validImageList.begin(), validImageList.end());
  127. return validImageList;
  128. }
  129. void backupVault(const string& vault,
  130. KindConfig conf /*Copy!*/ ,
  131. const DateTime& imageTime,
  132. bool fullImage)
  133. {
  134. if (!quiet)
  135. cout << DateTime::now().getString('h') << ": Backup of vault " << vault << endl;
  136. try
  137. {
  138. sizes[vault].second = 0; // nothing backed up yet
  139. string vaultpath = findVault(vault);
  140. const string& vaultConfigName = vaultpath + '/' + conf.getString("vaultConfigName");
  141. readVaultConfig(vaultConfigName, conf);
  142. if (debug)
  143. {
  144. cout << "vault config:" << endl;
  145. conf.print();
  146. }
  147. string imageName = getImageName(conf);
  148. if (!imageName.empty())
  149. imageName += '-';
  150. string imageFullName = vaultpath + "/" + imageName ;
  151. if (conf.getBool("longImageName"))
  152. imageFullName += imageTime.getString('m');
  153. else
  154. imageFullName += imageTime.getString('s');
  155. verbosePrint("backup to \"" + imageFullName + "\"");
  156. // find reference image
  157. string referenceImage;
  158. if (!fullImage)
  159. {
  160. Strings validImageList = findValidImages(vaultpath, conf);
  161. // last image is newest image
  162. referenceImage = validImageList.back();
  163. }
  164. // create image path
  165. if (!dryRun)
  166. if (mkdir(imageFullName.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
  167. throw Exception("Create image", "failed to create " + imageFullName);
  168. // error message
  169. // we write an generic error message to mark backup as unsuccessful
  170. // will be deleted at successful end of rsync
  171. string errorfile = imageFullName + "/error";
  172. if (!dryRun)
  173. {
  174. ofstream error(errorfile);
  175. error << "failed" << endl;
  176. error.close();
  177. }
  178. string host;
  179. if (conf.hasKey("host"))
  180. host = conf.getString("host");
  181. string server;
  182. if (conf.hasKey("server"))
  183. server = conf.getString("server");
  184. if (!host.empty() && !server.empty())
  185. throw Exception("backupVault", "Cannot have host and server");
  186. string path = conf.getString("path");
  187. if (path.empty())
  188. throw Exception("rsync", "empty source path");
  189. if (path.back() != '/')
  190. path += '/';
  191. string rsyncCmd = "rsync -vrltH --delete --stats -D --numeric-ids ";
  192. if (!conf.getBool("ignorePermission"))
  193. rsyncCmd += "-pgo";
  194. vector<string> rso = conf.getStrings("rsyncOption");
  195. for (string opt : rso)
  196. rsyncCmd += opt + " ";
  197. if (!host.empty()) // shell mode
  198. {
  199. // cout << "USING SHELLMODE '" << host << "'" << endl;
  200. string remoteShell = conf.getString("remoteShell");
  201. string userAtHost = conf.getString("user") + "@" +
  202. conf.getString("host");
  203. string rshCommand = remoteShell;
  204. if (remoteShell.empty())
  205. rshCommand = "ssh";
  206. rshCommand += " " + userAtHost;
  207. // excludes
  208. Strings excluded;
  209. string userExcludeCommand = conf.getString("userExcludeCommand");
  210. if (!userExcludeCommand.empty())
  211. {
  212. replacePlaceHolder(userExcludeCommand, "%path", path);
  213. string excludeCommand = rshCommand + " " + userExcludeCommand;
  214. verbosePrint("searching for exclusions (" + excludeCommand + ")");
  215. int rc;
  216. excluded = myPopen(excludeCommand, rc, debug);
  217. if (rc > 0)
  218. throw Exception("Find exludes", "Search for excludes failed");
  219. for (unsigned int i = 0; i < excluded.size(); ++i)
  220. {
  221. FileName fn(excluded[i]);
  222. excluded[i] = '/' + fn.getPath();
  223. debugPrint("Excluding: " + excluded[i]);
  224. }
  225. }
  226. string userExcludeFile = conf.getString("userExcludeFile");
  227. if (!userExcludeFile.empty())
  228. {
  229. userExcludeFile = path + userExcludeFile;
  230. string getExcludeFileCommand = rshCommand;
  231. getExcludeFileCommand += " \" if [ -f '" + userExcludeFile + "' ]; then ";
  232. getExcludeFileCommand += " cat '" + userExcludeFile + "' ; fi \"";
  233. // cout << getExcludeFileCommand << endl;
  234. int rc;
  235. Strings excludes2 = myPopen(getExcludeFileCommand, rc, debug);
  236. if (rc == 0)
  237. excluded += excludes2;
  238. }
  239. if (conf.hasKey("exclude"))
  240. excluded += conf.getStrings("exclude");
  241. if (!dryRun)
  242. strings2File(excluded, imageFullName + "/exclude");
  243. // rsync image
  244. if (!remoteShell.empty())
  245. rsyncCmd += " -e \'" + remoteShell + "\' ";
  246. rsyncCmd += "--exclude-from=" + imageFullName + "/exclude ";
  247. if (!referenceImage.empty())
  248. rsyncCmd += "--link-dest=" + referenceImage + "/tree ";
  249. rsyncCmd += userAtHost + ":" + path + " ";
  250. rsyncCmd += imageFullName + "/tree";
  251. } // shell mode
  252. else
  253. {
  254. // cout << "USING SERVERMODE" << endl;
  255. vector<string> excluded;
  256. if (conf.hasKey("exclude"))
  257. {
  258. Strings excludes = conf.getStrings("exclude");
  259. for (string s : excludes)
  260. excluded.push_back(s);
  261. }
  262. if (!dryRun)
  263. strings2File(excluded, imageFullName + "/exclude");
  264. rsyncCmd += "--exclude-from=" + imageFullName + "/exclude ";
  265. if (!referenceImage.empty())
  266. rsyncCmd += "--link-dest=" + referenceImage + "/tree ";
  267. rsyncCmd += conf.getString("server") + "::" + path + " ";
  268. rsyncCmd += imageFullName + "/tree";
  269. }
  270. debugPrint("Action: " + rsyncCmd);
  271. vector<string> backupResult;
  272. if (!dryRun)
  273. {
  274. verbosePrint("syncing (" + rsyncCmd + ")");
  275. int rc;
  276. backupResult = myPopen(rsyncCmd, rc, debug, imageFullName + "/rsync-log");
  277. // strings2File(backupResult, imageFullName + "/rsync-log");
  278. if (rc == 0 || rc == 24) // "no error" or "vanished source files" (ignored)
  279. {
  280. unlink(errorfile.c_str());
  281. string lastLink = vaultpath + "/last";
  282. unlink(lastLink.c_str());
  283. symlink(imageFullName.c_str(), lastLink.c_str());
  284. long int st = 0;
  285. long int sc = 0;
  286. for (auto bl : backupResult)
  287. {
  288. if (bl.substr(0, 15) == "Total file size")
  289. st = getNumber(bl);
  290. else if (bl.substr(0, 27) == "Total transferred file size")
  291. sc = getNumber(bl);
  292. }
  293. // sizes[vault] = pair<long int, long int>(st, sc);
  294. sizes[vault] = Sizes(st, sc);
  295. // cout << vault << " " << st << " || " << sc << endl;
  296. }
  297. else
  298. throw Exception("Backup", "Failed to execute rsync");
  299. }
  300. else
  301. cout << "Not executing " << rsyncCmd << endl;
  302. }
  303. catch (Exception ex)
  304. {
  305. cerr << "Exception in vault " << vault << ": " << ex.what() << endl;
  306. }
  307. }
  308. DateTime imageDate(const string& image)
  309. {
  310. FileName fn(image);
  311. Strings ss;
  312. split(fn.getName(), ss, '-');
  313. if (ss.size() < 5)
  314. throw Exception("imageDate", "image date not available");
  315. int Y = stoi(ss[1]);
  316. int M = stoi(ss[2]);
  317. int D = stoi(ss[3]);
  318. int h = stoi(ss[4]);
  319. int m = 0, s = 0;
  320. if (ss.size() > 5) // longImageName
  321. m = stoi(ss[5]);
  322. if (ss.size() > 6)
  323. s = stoi(ss[6]);
  324. return DateTime(Y, M, D, h, m, s);
  325. }
  326. void parseRule(string rule,
  327. set<int>& M, set<int>& D, set<int>& W, set<int>& h,
  328. time_t& exptime)
  329. {
  330. for (unsigned int i = 0; i < rule.size(); ++i)
  331. rule[i] = tolower(rule[i]);
  332. substitute(rule, ' ', ',');
  333. reduceToOne(rule, ',');
  334. // rule = hour wday mday month <exptime>
  335. Lexer p(rule);
  336. h = getValues(p, 0, 23); // hour
  337. p.expect(',');
  338. W = getValues(p, 0, 7, 1); // wday
  339. p.expect(',');
  340. D = getValues(p, 1, 31); // day of month
  341. p.expect(',');
  342. M = getValues(p, 1, 12, 2); // month
  343. #if 0
  344. // debug-output
  345. cout << "hour: ";
  346. for (int i : h)
  347. cout << i << " ";
  348. cout << endl;
  349. cout << "wday: ";
  350. for (int i : W)
  351. cout << i << " ";
  352. cout << endl;
  353. cout << "mday: ";
  354. for (int i : D)
  355. cout << i << " ";
  356. cout << endl;
  357. cout << "month: ";
  358. for (int i : M)
  359. cout << i << " ";
  360. cout << endl;
  361. #endif
  362. string ts = p.getAll();
  363. substitute(ts, ',', ' ');
  364. exptime = stot(ts);
  365. }
  366. int removeDir(const string& path)
  367. {
  368. debugPrint("removeDir " + path);
  369. DIR* d = opendir(path.c_str());
  370. int r = -1;
  371. if (d)
  372. {
  373. struct dirent* p;
  374. r = 0;
  375. while (!r && (p = readdir(d)))
  376. {
  377. int r2 = 0;
  378. string fn = p->d_name;
  379. if (fn != "." && fn != "..")
  380. {
  381. fn = path + "/" + fn;
  382. debugPrint("-- " + fn);
  383. struct stat statbuf;
  384. if (lstat(fn.c_str(), &statbuf) == 0)
  385. {
  386. if (S_ISLNK(statbuf.st_mode))
  387. {
  388. debugPrint("Remove link " + fn);
  389. r2 = unlink(fn.c_str());
  390. }
  391. else if (S_ISDIR(statbuf.st_mode))
  392. {
  393. debugPrint("Remove dir " + fn);
  394. r2 = removeDir(fn);
  395. }
  396. else
  397. {
  398. debugPrint("Remove file " + fn);
  399. r2 = unlink(fn.c_str());
  400. }
  401. }
  402. else
  403. {
  404. cout << "stat(" << fn << ") failed" << endl;
  405. // we assume "file" here
  406. r2 = unlink(fn.c_str());
  407. }
  408. }
  409. r = r2;
  410. }
  411. closedir(d);
  412. }
  413. if (r == 0)
  414. {
  415. debugPrint("Remove Dir itself " + path);
  416. r = rmdir(path.c_str());
  417. }
  418. return r;
  419. }
  420. #if 0
  421. int removeDir(const string& dname)
  422. {
  423. int rc = 0;
  424. if (!dryRun)
  425. {
  426. Strings files;
  427. // subdirectories
  428. dirList(dname, files);
  429. for (unsigned int i = 0; i < files.size(); ++i)
  430. {
  431. debugPrint("Remove dir " + files[i]);
  432. for (unsigned int i = 0; i < files.size(); ++i)
  433. rc += removeDir(files[i]);
  434. }
  435. files.clear();
  436. // files in directory
  437. fileList(dname, files);
  438. for (unsigned int i = 0; i < files.size(); ++i)
  439. {
  440. debugPrint("unlink " + files[i]);
  441. if (!dryRun)
  442. {
  443. if (unlink(files[i].c_str()) != 0)
  444. rc++;
  445. }
  446. }
  447. debugPrint("rmdir " + dname);
  448. // directory
  449. if (rmdir(dname.c_str()) != 0)
  450. rc++;
  451. }
  452. return rc;
  453. }
  454. #endif
  455. void expireVault(const string& vault, KindConfig conf, DateTime now)
  456. {
  457. if (!quiet)
  458. cout << DateTime::now().getString('h') << ": Expiring images in vault " << vault << endl;
  459. string vaultpath = findVault(vault);
  460. debugPrint("searching images in " + vaultpath);
  461. const string& vaultConfigName = vaultpath + '/' + conf.getString("vaultConfigName");
  462. readVaultConfig(vaultConfigName, conf);
  463. if (debug)
  464. {
  465. cout << "vault config:" << endl;
  466. conf.print();
  467. }
  468. Strings dirlist; // list of subdirectories
  469. dirList(vaultpath, dirlist);
  470. Strings validImages;
  471. Strings invalidImages;
  472. string imgname = getImageName(conf);
  473. for (unsigned int i = 0; i < dirlist.size(); ++i)
  474. {
  475. FileName fn(dirlist[i]);
  476. if (startsWith(fn.getName(), imgname)) // dir is image ?
  477. {
  478. debugPrint(dirlist[i]);
  479. DateTime t = imageDate(dirlist[i]);
  480. if (t != now) // ignore just created image
  481. {
  482. if (!isValidImage(dirlist[i])) // invalid image?
  483. {
  484. invalidImages.push_back(dirlist[i]);
  485. debugPrint("- invalid image");
  486. }
  487. else
  488. {
  489. validImages.push_back(dirlist[i]);
  490. debugPrint("- valid image");
  491. }
  492. }
  493. else
  494. debugPrint("- current image - ignored");
  495. }
  496. }
  497. for (unsigned int i = 0; i < invalidImages.size(); ++i)
  498. {
  499. try
  500. {
  501. DateTime t = imageDate(invalidImages[i]);
  502. DateTime expireTime = t + stot(conf.getString("expireFailedImage"));
  503. if (debug)
  504. {
  505. cout << "image: " << t.getString('h') << " expire: " << expireTime.getString('h') << endl;
  506. cout << " now: " << now.getString('h') << endl;
  507. }
  508. if (expireTime < now)
  509. {
  510. if (!quiet)
  511. cout << " removing invalid image " << invalidImages[i] << endl;
  512. if (removeDir(invalidImages[i]) != 0)
  513. cout << "Error removing " << invalidImages[i] << endl;
  514. }
  515. }
  516. catch (Exception ex)
  517. {
  518. cerr << "Exception: " << ex.what() << endl;
  519. }
  520. }
  521. sort(validImages.begin(), validImages.end()); // lexicographical order == temporal order
  522. for (unsigned int i = 0;
  523. i < validImages.size() - 1; // never expire latest image
  524. ++i)
  525. {
  526. try
  527. {
  528. DateTime imageTime = imageDate(validImages[i]);
  529. DateTime expireTime = DateTime::now() + 100; // don't expire if no rule found
  530. Strings expireRules = conf.getStrings("expireRule");
  531. int ruleNr = 0;
  532. for (unsigned int k = 0; k < expireRules.size(); ++k)
  533. {
  534. debugPrint("Checking rule " + expireRules[k]);
  535. set<int> M, D, W, h;
  536. set<int> Y, m, s;
  537. time_t expirePeriod;
  538. parseRule(expireRules[k], M, D, W, h, expirePeriod);
  539. // cout << M << " " << D << " " << W << " " << h << " " << expirePeriod << endl;
  540. if (imageTime.match(Y, M, D, W, h, m, s))
  541. {
  542. debugPrint("match");
  543. expireTime = imageTime + expirePeriod;
  544. ruleNr = k;
  545. }
  546. }
  547. if (debug)
  548. {
  549. cout << "image: " << imageTime.getString('h') << " expire: " << expireTime.getString('h') << endl;
  550. cout << " now: " << now.getString('h') << endl;
  551. }
  552. if (now > expireTime)
  553. {
  554. if (!quiet)
  555. cout << "removing " << validImages[i] << " rule=" << expireRules[ruleNr] << endl;
  556. removeDir(validImages[i]);
  557. }
  558. }
  559. catch (Exception ex)
  560. {
  561. cerr << "Exception: " << ex.what() << endl;
  562. }
  563. }
  564. }
  565. /*AppGen:Main*/
  566. int main(int argc, char* argv[])
  567. {
  568. /*AppGen:MainEnd*/
  569. int exitCode = 0;
  570. string lockFile;
  571. try
  572. {
  573. if (debug)
  574. verbose = true;
  575. KindConfig conf;
  576. // default-values
  577. conf.add("imageName", "image");
  578. conf.add("vaultConfigName", "kind/vault.conf");
  579. conf.add("expireFailedImage", "3 days");
  580. conf.add("expireRule", "* * * * 1 month");
  581. conf.add("rsyncOption", ""); // no additional rsync option
  582. conf.add("remoteShell", "");
  583. conf.add("lockfile", "/var/lock/kind");
  584. conf.add("userExcludeFile", "nobackup.list");
  585. conf.add("userExcludeCommand",
  586. "find %path -type f -iname '*nobackup' -printf '%P\\\\n'");
  587. conf.add("logSize", "");
  588. if (!masterConfig.empty())
  589. readMasterConfig(masterConfig, conf);
  590. else if (fileExists("/etc/kind/master.conf"))
  591. readMasterConfig("etc/kind/master.conf", conf);
  592. else if (fileExists("/ffp/etc/kind/master.conf"))
  593. readMasterConfig("/ffp/etc/kind/master.conf", conf);
  594. else
  595. throw Exception("MasterConfig", "no file");
  596. if (debug)
  597. {
  598. cout << "global config:" << endl;
  599. conf.print();
  600. }
  601. lockFile = conf.getString("lockfile");
  602. createLock(lockFile);
  603. DateTime imageTime = DateTime::now();
  604. string logSizeFile = conf.getString("logSize");
  605. if (!logSizeFile.empty() && fileExists(logSizeFile))
  606. {
  607. vector<string> ss;
  608. file2Strings(logSizeFile, ss);
  609. for (auto s : ss)
  610. {
  611. unsigned int i = 0;
  612. string v = getWord(s, i);
  613. long int s1 = getLongInt(s, i);
  614. long int s2 = getLongInt(s, i);
  615. sizes[v] = Sizes(s1, s2);
  616. }
  617. }
  618. vector<string> vaults;
  619. string groupname = "group_" + vault;
  620. if (conf.hasKey(groupname))
  621. vaults = conf.getStrings(groupname);
  622. else
  623. vaults.push_back(vault);
  624. if (!expireOnly)
  625. for (unsigned int i = 0; i < vaults.size(); ++i)
  626. {
  627. backupVault(vaults[i], conf, imageTime, fullImage);
  628. if (!logSizeFile.empty())
  629. {
  630. Strings st;
  631. for (auto s : sizes)
  632. {
  633. string h = s.first + " " + to_string(s.second.first) + " " + to_string(s.second.second);
  634. st.push_back(h);
  635. }
  636. strings2File(st, logSizeFile);
  637. }
  638. }
  639. if (!backupOnly)
  640. for (unsigned int i = 0; i < vaults.size(); ++i)
  641. expireVault(vaults[i], conf, imageTime);
  642. if (!quiet)
  643. cout << DateTime::now().getString('h') << ": finished" << endl;
  644. }
  645. catch (const Exception& ex)
  646. {
  647. cerr << "Exception: " << ex.what() << endl;
  648. exitCode = 1;
  649. }
  650. catch (const char* msg)
  651. {
  652. cerr << "Exception(char*): " << msg << endl;
  653. exitCode = 1;
  654. }
  655. catch (const string& msg)
  656. {
  657. cerr << "Exception(string): " << msg << endl;
  658. exitCode = 1;
  659. }
  660. removeLock(lockFile);
  661. return exitCode;
  662. }