source: box/trunk/bin/bbackupctl/bbackupctl.cpp @ 2694

Revision 2694, 7.4 KB checked in by chris, 2 years ago (diff)

Replace BOX_FILE_BBACKUPD_DEFAULT_CONFIG with
BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    bbackupctl.cpp
5//              Purpose: bbackupd daemon control program
6//              Created: 18/2/04
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <cstdio>
13#include <cstdlib>
14
15#ifdef HAVE_UNISTD_H
16        #include <unistd.h>
17#endif
18
19#include <cstdlib>
20
21#include "MainHelper.h"
22#include "BoxPortsAndFiles.h"
23#include "BackupDaemonConfigVerify.h"
24#include "Socket.h"
25#include "SocketStream.h"
26#include "IOStreamGetLine.h"
27
28#ifdef WIN32
29        #include "WinNamedPipeStream.h"
30#endif
31
32#include "MemLeakFindOn.h"
33
34enum Command
35{
36        Default,
37        WaitForSyncStart,
38        WaitForSyncEnd,
39        SyncAndWaitForEnd,
40};
41
42void PrintUsageAndExit()
43{
44        printf("Usage: bbackupctl [-q] [-c config_file] <command>\n"
45        "Commands are:\n"
46        "  sync -- start a synchronisation (backup) run now\n"
47        "  force-sync -- force the start of a synchronisation run, "
48        "even if SyncAllowScript says no\n"
49        "  reload -- reload daemon configuration\n"
50        "  terminate -- terminate daemon now\n"
51        "  wait-for-sync -- wait until the next sync starts, then exit\n"
52        "  wait-for-end  -- wait until the next sync finishes, then exit\n"
53        "  sync-and-wait -- start sync, wait until it finishes, then exit\n"
54        );
55        exit(1);
56}
57
58int main(int argc, const char *argv[])
59{
60        int returnCode = 0;
61
62        MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT("bbackupctl.memleaks", 
63                "bbackupctl")
64
65        MAINHELPER_START
66
67        Logging::SetProgramName("bbackupctl");
68
69        // Filename for configuration file?
70        std::string configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE;
71       
72        // Quiet?
73        bool quiet = false;
74       
75        // See if there's another entry on the command line
76        int c;
77        while((c = getopt(argc, (char * const *)argv, "qc:l:")) != -1)
78        {
79                switch(c)
80                {
81                case 'q':
82                        // Quiet mode
83                        quiet = true;
84                        break;
85               
86                case 'c':
87                        // store argument
88                        configFilename = optarg;
89                        break;
90               
91                case '?':
92                default:
93                        PrintUsageAndExit();
94                }
95        }
96        // Adjust arguments
97        argc -= optind;
98        argv += optind;
99       
100        // Check there's a command
101        if(argc != 1)
102        {
103                PrintUsageAndExit();
104        }
105
106        // Read in the configuration file
107        if(!quiet) BOX_NOTICE("Using configuration file " << configFilename);
108
109        std::string errs;
110        std::auto_ptr<Configuration> config(
111                Configuration::LoadAndVerify
112                        (configFilename, &BackupDaemonConfigVerify, errs));
113
114        if(config.get() == 0 || !errs.empty())
115        {
116                BOX_ERROR("Invalid configuration file: " << errs);
117                return 1;
118        }
119        // Easier coding
120        const Configuration &conf(*config);
121
122        // Check there's a socket defined in the config file
123        if(!conf.KeyExists("CommandSocket"))
124        {
125                BOX_ERROR("Daemon isn't using a control socket, "
126                        "could not execute command.\n"
127                        "Add a CommandSocket declaration to the "
128                        "bbackupd.conf file.");
129                return 1;
130        }
131       
132        // Connect to socket
133
134#ifndef WIN32
135        SocketStream connection;
136#else /* WIN32 */
137        WinNamedPipeStream connection;
138#endif /* ! WIN32 */
139       
140        try
141        {
142#ifdef WIN32
143                std::string socket = conf.GetKeyValue("CommandSocket");
144                connection.Connect(socket);
145#else
146                connection.Open(Socket::TypeUNIX, conf.GetKeyValue("CommandSocket").c_str());
147#endif
148        }
149        catch(...)
150        {
151                BOX_ERROR("Failed to connect to daemon control socket.\n"
152                        "Possible causes:\n"
153                        "  * Daemon not running\n"
154                        "  * Daemon busy syncing with store server\n"
155                        "  * Another bbackupctl process is communicating with the daemon\n"
156                        "  * Daemon is waiting to recover from an error"
157                );
158
159                return 1;
160        }
161       
162        // For receiving data
163        IOStreamGetLine getLine(connection);
164       
165        // Wait for the configuration summary
166        std::string configSummary;
167        if(!getLine.GetLine(configSummary))
168        {
169                BOX_ERROR("Failed to receive configuration summary "
170                        "from daemon");
171                return 1;
172        }
173
174        // Was the connection rejected by the server?
175        if(getLine.IsEOF())
176        {
177                BOX_ERROR("Server rejected the connection. Are you running "
178                        "bbackupctl as the same user as the daemon?");
179                return 1;
180        }
181
182        // Decode it
183        int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
184        if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d", &autoBackup,
185                        &updateStoreInterval, &minimumFileAge, &maxUploadWait) != 4)
186        {
187                BOX_ERROR("Config summary didn't decode.");
188                return 1;
189        }
190        // Print summary?
191        if(!quiet)
192        {
193                BOX_INFO("Daemon configuration summary:\n"
194                        "  AutomaticBackup = " << 
195                        (autoBackup?"true":"false") << "\n"
196                        "  UpdateStoreInterval = " << updateStoreInterval << 
197                        " seconds\n"
198                        "  MinimumFileAge = " << minimumFileAge << " seconds\n"
199                        "  MaxUploadWait = " << maxUploadWait << " seconds");
200        }
201
202        std::string stateLine;
203        if(!getLine.GetLine(stateLine) || getLine.IsEOF())
204        {
205                BOX_ERROR("Failed to receive state line from daemon");
206                return 1;
207        }
208
209        // Decode it
210        int currentState;
211        if(::sscanf(stateLine.c_str(), "state %d", &currentState) != 1)
212        {
213                BOX_ERROR("Received invalid state line from daemon");
214                return 1;
215        }
216
217        Command command = Default;
218        std::string commandName(argv[0]);
219
220        if (commandName == "wait-for-sync")
221        {
222                command = WaitForSyncStart;
223        }
224        else if (commandName == "wait-for-end")
225        {
226                command = WaitForSyncEnd;
227        }
228        else if (commandName == "sync-and-wait")
229        {
230                command = SyncAndWaitForEnd;
231        }
232
233        switch (command)
234        {
235                case WaitForSyncStart:
236                case WaitForSyncEnd:
237                {
238                        // Check that it's in automatic mode,
239                        // because otherwise it'll never start
240
241                        if(!autoBackup)
242                        {
243                                BOX_ERROR("Daemon is not in automatic mode, "
244                                        "sync will never start!");
245                                return 1;
246                        }
247
248                }
249                break;
250
251                case SyncAndWaitForEnd:
252                {
253                        // send a sync command
254                        commandName = "force-sync";
255                        std::string cmd = commandName + "\n";
256                        connection.Write(cmd.c_str(), cmd.size());
257                        connection.WriteAllBuffered();
258
259                        if (currentState != 0)
260                        {
261                                BOX_INFO("Waiting for current sync/error state "
262                                        "to finish...");
263                        }
264                }
265                break;
266
267                default:
268                {
269                        // Normal case, just send the command given
270                        // plus a quit command.
271                        std::string cmd = commandName;
272                        cmd += "\nquit\n";
273                        connection.Write(cmd.c_str(), cmd.size());
274                }
275        }
276       
277        // Read the response
278        std::string line;
279        bool syncIsRunning = false;
280        bool finished = false;
281
282        while(!finished && !getLine.IsEOF() && getLine.GetLine(line))
283        {
284                switch (command)
285                {
286                        case WaitForSyncStart:
287                        {
288                                // Need to wait for the state change...
289                                if(line == "start-sync")
290                                {
291                                        // Send a quit command to finish nicely
292                                        connection.Write("quit\n", 5);
293                                       
294                                        // And we're done
295                                        finished = true;
296                                }
297                        }
298                        break;
299
300                        case WaitForSyncEnd:
301                        case SyncAndWaitForEnd:
302                        {
303                                if(line == "start-sync")
304                                {
305                                        if (!quiet) BOX_INFO("Sync started...");
306                                        syncIsRunning = true;
307                                }
308                                else if(line == "finish-sync")
309                                {
310                                        if (syncIsRunning)
311                                        {
312                                                if (!quiet) BOX_INFO("Sync finished.");
313                                                // Send a quit command to finish nicely
314                                                connection.Write("quit\n", 5);
315                                       
316                                                // And we're done
317                                                finished = true;
318                                        }
319                                        else
320                                        {
321                                                if (!quiet) BOX_INFO("Previous sync finished.");
322                                        }
323                                        // daemon must still be busy
324                                }
325                        }
326                        break;
327
328                        default:
329                        {
330                                // Is this an OK or error line?
331                                if(line == "ok")
332                                {
333                                        if(!quiet)
334                                        {
335                                                BOX_INFO("Control command "
336                                                        "sent: " <<
337                                                        commandName);
338                                        }
339                                        finished = true;
340                                }
341                                else if(line == "error")
342                                {
343                                        BOX_ERROR("Control command failed: " <<
344                                                commandName << ". Check "
345                                                "command spelling.");
346                                        returnCode = 1;
347                                        finished = true;
348                                }
349                        }
350                }
351        }
352
353        MAINHELPER_END
354
355#if defined WIN32 && ! defined BOX_RELEASE_BUILD
356        closelog();
357#endif
358       
359        return returnCode;
360}
Note: See TracBrowser for help on using the repository browser.