1 /**
2 Copyright: Copyright (c) 2019, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module dsnapshot.cmdgroup.remote;
7 
8 import logger = std.experimental.logger;
9 
10 import dsnapshot.config;
11 import dsnapshot.types;
12 
13 int cmdRemote(const Config.Remotecmd conf) nothrow {
14     import std.algorithm : map, filter;
15     import std.exception : collectException;
16     import std.file : dirEntries, SpanMode, exists, mkdirRecurse, rmdirRecurse, rename;
17     import std.path : baseName;
18     import std.stdio : writeln;
19 
20     final switch (conf.cmd) {
21     case RemoteSubCmd.none:
22         break;
23     case RemoteSubCmd.lsDirs:
24         try {
25             foreach (p; dirEntries(conf.path, SpanMode.shallow).filter!(a => a.isDir)
26                     .map!(a => a.name.baseName)) {
27                 writeln(p);
28             }
29         } catch (Exception e) {
30         }
31         break;
32     case RemoteSubCmd.mkdirRecurse:
33         try {
34             if (!exists(conf.path))
35                 mkdirRecurse(conf.path);
36         } catch (Exception e) {
37             logger.warning(e.msg).collectException;
38         }
39         break;
40     case RemoteSubCmd.rmdirRecurse:
41         if (!exists(conf.path))
42             return 0;
43 
44         try {
45             // can fail because a directory is write protected.
46             rmdirRecurse(conf.path);
47             return 0;
48         } catch (Exception e) {
49         }
50 
51         try {
52             foreach (const p; dirEntries(conf.path, SpanMode.depth).filter!(a => a.isDir)) {
53                 import core.sys.posix.sys.stat;
54                 import std.file : getAttributes, setAttributes;
55 
56                 const attrs = getAttributes(p);
57                 setAttributes(p, attrs | S_IRWXU);
58             }
59             rmdirRecurse(conf.path);
60         } catch (Exception e) {
61             logger.warning(e.msg).collectException;
62         }
63         break;
64     case RemoteSubCmd.publishSnapshot:
65         if (!exists(conf.path)) {
66             logger.infof("Snapshot %s do not exist", conf.path).collectException;
67             return 1;
68         }
69 
70         publishSnapshot(conf.path);
71         break;
72     case RemoteSubCmd.fakerootStats:
73         import std.stdio : File;
74         import std.path : buildPath;
75         import dsnapshot.stats;
76 
77         const fakerootEnv = buildPath(conf.path, snapshotFakerootEnv);
78         if (!exists(fakerootEnv)) {
79             logger.infof("Unable to find or open %s", conf.path).collectException;
80             return 1;
81         }
82         try {
83             auto fkdb = fromFakerootEnv(fakerootEnv.Path);
84             writeln; // make sure we start at a new line
85             foreach (const ps; fromFakeroot(fkdb, conf.path, buildPath(conf.path, snapshotData)))
86                 writeln(ps.toString);
87         } catch (Exception e) {
88             logger.warning(e.msg).collectException;
89         }
90     }
91     return 0;
92 }
93 
94 /// Publish a snapshot that has the status "in-progress".
95 int publishSnapshot(const string snapshot) nothrow @safe {
96     import std.algorithm : map, filter;
97     import std.exception : collectException;
98     import std.file : dirEntries, SpanMode, exists, mkdirRecurse, rmdirRecurse, rename;
99     import std.path : baseName;
100     import std.stdio : writeln;
101 
102     const dst = () {
103         if (snapshot.length < snapshotInProgressSuffix.length)
104             return null;
105         return snapshot[0 .. $ - snapshotInProgressSuffix.length];
106     }();
107 
108     if (exists(dst)) {
109         logger.errorf("Destination %s already exist thus unable to publish snapshot %s",
110                 dst, snapshot).collectException;
111         return 1;
112     }
113 
114     try {
115         rename(snapshot, dst);
116     } catch (Exception e) {
117         logger.error(e.msg).collectException;
118         return 1;
119     }
120 
121     return 0;
122 }