Fossil SCM

fossil-scm / src / backoffice.c
Blame History Raw 882 lines
1
/*
2
** Copyright (c) 2018 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to manage a background processes that
19
** occur after user interaction with the repository. Examples of
20
** backoffice processing includes:
21
**
22
** * Sending alerts and notifications
23
** * Processing the email queue
24
** * Handling post-receive hooks
25
** * Automatically syncing to peer repositories
26
**
27
** Backoffice processing is automatically started whenever there are
28
** changes to the repository. The backoffice process dies off after
29
** a period of inactivity.
30
**
31
** Steps are taken to ensure that only a single backoffice process is
32
** running at a time. Otherwise, there could be race conditions that
33
** cause adverse effects such as multiple alerts for the same changes.
34
**
35
** At the same time, we do not want a backoffice process to run forever.
36
** Backoffice processes should die off after doing whatever work they need
37
** to do. In this way, we avoid having lots of idle processes in the
38
** process table, doing nothing on rarely accessed repositories, and
39
** if the Fossil binary is updated on a system, the backoffice processes
40
** will restart using the new binary automatically.
41
**
42
** At any point in time there should be at most two backoffice processes.
43
** There is a main process that is doing the actual work, and there is
44
** a second stand-by process that is waiting for the main process to finish
45
** and that will become the main process after a delay.
46
**
47
** After any successful web page reply, the backoffice_check_if_needed()
48
** routine is called. That routine checks to see if both one or both of
49
** the backoffice processes are already running. That routine remembers the
50
** status in a global variable.
51
**
52
** Later, after the repository database is closed, the
53
** backoffice_run_if_needed() routine is called. If the prior call
54
** to backoffice_check_if_needed() indicated that backoffice processing
55
** might be required, the run_if_needed() attempts to kick off a backoffice
56
** process.
57
**
58
** All work performed by the backoffice is in the backoffice_work()
59
** routine.
60
*/
61
#if defined(_WIN32)
62
# if defined(_WIN32_WINNT)
63
# undef _WIN32_WINNT
64
# endif
65
# define _WIN32_WINNT 0x501
66
#endif
67
#include "config.h"
68
#include "backoffice.h"
69
#include <time.h>
70
#if defined(_WIN32)
71
# include <windows.h>
72
# include <stdio.h>
73
# include <process.h>
74
# if defined(__MINGW32__)
75
# include <wchar.h>
76
# endif
77
# define GETPID (int)GetCurrentProcessId
78
#else
79
# include <unistd.h>
80
# include <sys/types.h>
81
# include <signal.h>
82
# include <errno.h>
83
# include <sys/time.h>
84
# include <sys/resource.h>
85
# include <fcntl.h>
86
# define GETPID getpid
87
#endif
88
#include <time.h>
89
90
/*
91
** The BKOFCE_LEASE_TIME is the amount of time for which a single backoffice
92
** processing run is valid. Each backoffice run monopolizes the lease for
93
** at least this amount of time. Hopefully all backoffice processing is
94
** finished much faster than this - usually in less than a second. But
95
** regardless of how long each invocation lasts, successive backoffice runs
96
** must be spaced out by at least this much time.
97
*/
98
#define BKOFCE_LEASE_TIME 60 /* Length of lease validity in seconds */
99
100
#if LOCAL_INTERFACE
101
/*
102
** An instance of the following object describes a lease on the backoffice
103
** processing timeslot. This lease is used to help ensure that no more than
104
** one process is running backoffice at a time.
105
*/
106
struct Lease {
107
sqlite3_uint64 idCurrent; /* process ID for the current lease holder */
108
sqlite3_uint64 tmCurrent; /* Expiration of the current lease */
109
sqlite3_uint64 idNext; /* process ID for the next lease holder on queue */
110
sqlite3_uint64 tmNext; /* Expiration of the next lease */
111
};
112
#endif
113
114
/***************************************************************************
115
** Local state variables
116
**
117
** Set to prevent backoffice processing from ever entering sleep or
118
** otherwise taking a long time to complete. Set this when a user-visible
119
** process might need to wait for backoffice to complete.
120
*/
121
static int backofficeNoDelay = 0;
122
123
/* This variable is set to the name of a database on which backoffice
124
** should run if backoffice process is needed. It is set by the
125
** backoffice_check_if_needed() routine which must be run while the database
126
** file is open. Later, after the database is closed, the
127
** backoffice_run_if_needed() will consult this variable to see if it
128
** should be a no-op.
129
**
130
** The magic string "x" in this variable means "do not run the backoffice".
131
*/
132
static char *backofficeDb = 0;
133
134
/*
135
** Log backoffice activity to a file named here. If not NULL, this
136
** overrides the "backoffice-logfile" setting of the database. If NULL,
137
** the "backoffice-logfile" setting is used instead.
138
*/
139
static const char *backofficeLogfile = 0;
140
141
/*
142
** Write the log message into this open file.
143
*/
144
static FILE *backofficeFILE = 0;
145
146
/*
147
** Write backoffice log messages on this BLOB. to this connection:
148
*/
149
static Blob *backofficeBlob = 0;
150
151
/*
152
** Non-zero for extra logging detail.
153
*/
154
static int backofficeLogDetail = 0;
155
156
/* End of state variables
157
****************************************************************************/
158
159
/*
160
** This function emits a diagnostic message related to the processing in
161
** this module.
162
*/
163
static void backofficeTrace(const char *zFormat, ...){
164
char *zMsg = 0;
165
if( g.fAnyTrace ){
166
va_list ap;
167
va_start(ap, zFormat);
168
zMsg = sqlite3_vmprintf(zFormat, ap);
169
va_end(ap);
170
}
171
if( g.fAnyTrace ) fprintf(stderr, "%s", zMsg);
172
if( zMsg ) sqlite3_free(zMsg);
173
}
174
175
/*
176
** Do not allow backoffice processes to sleep waiting on a timeslot.
177
** They must either do their work immediately or exit.
178
**
179
** In a perfect world, this interface would not exist, as there would
180
** never be a problem with waiting backoffice threads. But in some cases
181
** a backoffice will delay a UI thread, so we don't want them to run for
182
** longer than needed.
183
*/
184
void backoffice_no_delay(void){
185
backofficeNoDelay = 1;
186
}
187
188
/*
189
** Sleeps for the specified number of milliseconds -OR- until interrupted
190
** by another thread (if supported by the underlying platform). Non-zero
191
** will be returned if the sleep was interrupted.
192
*/
193
static int backofficeSleep(int milliseconds){
194
#if defined(_WIN32)
195
assert( milliseconds>=0 );
196
if( SleepEx((DWORD)milliseconds, TRUE)==WAIT_IO_COMPLETION ){
197
return 1;
198
}
199
#else
200
sqlite3_sleep(milliseconds);
201
#endif
202
return 0;
203
}
204
205
/*
206
** Parse a unsigned 64-bit integer from a string. Return a pointer
207
** to the character of z[] that occurs after the integer.
208
*/
209
static const char *backofficeParseInt(const char *z, sqlite3_uint64 *pVal){
210
*pVal = 0;
211
if( z==0 ) return 0;
212
while( fossil_isspace(z[0]) ){ z++; }
213
while( fossil_isdigit(z[0]) ){
214
*pVal = (*pVal)*10 + z[0] - '0';
215
z++;
216
}
217
return z;
218
}
219
220
/*
221
** Read the "backoffice" property and parse it into a Lease object.
222
**
223
** The backoffice property should consist of four integers:
224
**
225
** (1) Process ID for the active backoffice process.
226
** (2) Time (seconds since 1970) for when the active backoffice
227
** lease expires.
228
** (3) Process ID for the on-deck backoffice process.
229
** (4) Time when the on-deck process should expire.
230
**
231
** No other process should start active backoffice processing until
232
** process (1) no longer exists and the current time exceeds (2).
233
*/
234
static void backofficeReadLease(Lease *pLease){
235
Stmt q;
236
memset(pLease, 0, sizeof(*pLease));
237
db_unprotect(PROTECT_CONFIG);
238
db_prepare(&q, "SELECT value FROM repository.config"
239
" WHERE name='backoffice'");
240
if( db_step(&q)==SQLITE_ROW ){
241
const char *z = db_column_text(&q,0);
242
z = backofficeParseInt(z, &pLease->idCurrent);
243
z = backofficeParseInt(z, &pLease->tmCurrent);
244
z = backofficeParseInt(z, &pLease->idNext);
245
backofficeParseInt(z, &pLease->tmNext);
246
}
247
db_finalize(&q);
248
db_protect_pop();
249
}
250
251
/*
252
** Return a string that describes how long it has been since the
253
** last backoffice run. The string is obtained from fossil_malloc().
254
*/
255
char *backoffice_last_run(void){
256
Lease x;
257
sqlite3_uint64 tmNow;
258
double rAge;
259
backofficeReadLease(&x);
260
tmNow = time(0);
261
if( x.tmCurrent==0 ){
262
return fossil_strdup("never");
263
}
264
if( tmNow<=(x.tmCurrent-BKOFCE_LEASE_TIME) ){
265
return fossil_strdup("moments ago");
266
}
267
rAge = (tmNow - (x.tmCurrent-BKOFCE_LEASE_TIME))/86400.0;
268
return mprintf("%z ago", human_readable_age(rAge));
269
}
270
271
/*
272
** Write a lease to the backoffice property
273
*/
274
static void backofficeWriteLease(Lease *pLease){
275
db_unprotect(PROTECT_CONFIG);
276
db_multi_exec(
277
"REPLACE INTO repository.config(name,value,mtime)"
278
" VALUES('backoffice','%lld %lld %lld %lld',now())",
279
pLease->idCurrent, pLease->tmCurrent,
280
pLease->idNext, pLease->tmNext);
281
db_protect_pop();
282
}
283
284
/*
285
** Check to see if the specified Win32 process is still alive. It
286
** should be noted that even if this function returns non-zero, the
287
** process may die before another operation on it can be completed.
288
*/
289
#if defined(_WIN32)
290
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
291
# define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
292
#endif
293
static int backofficeWin32ProcessExists(DWORD dwProcessId){
294
HANDLE hProcess;
295
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,dwProcessId);
296
if( hProcess==NULL ) return 0;
297
CloseHandle(hProcess);
298
return 1;
299
}
300
#endif
301
302
/*
303
** Check to see if the process identified by pid is alive. If
304
** we cannot prove that the process is dead, return true.
305
*/
306
static int backofficeProcessExists(sqlite3_uint64 pid){
307
#if defined(_WIN32)
308
return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
309
#else
310
return pid>0 && kill((pid_t)pid, 0)==0;
311
#endif
312
}
313
314
/*
315
** Check to see if the process identified by pid has finished. If
316
** we cannot prove that the process is still running, return true.
317
*/
318
static int backofficeProcessDone(sqlite3_uint64 pid){
319
#if defined(_WIN32)
320
return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0;
321
#else
322
return pid<=0 || kill((pid_t)pid, 0)!=0;
323
#endif
324
}
325
326
/*
327
** Return a process id number for the current process
328
*/
329
static sqlite3_uint64 backofficeProcessId(void){
330
return (sqlite3_uint64)GETPID();
331
}
332
333
334
/*
335
** COMMAND: test-process-id
336
**
337
** Usage: %fossil [--sleep N] PROCESS-ID ...
338
**
339
** Show the current process id, and also tell whether or not all other
340
** processes IDs on the command line are running or not. If the --sleep N
341
** option is provide, then sleep for N seconds before exiting.
342
*/
343
void test_process_id_command(void){
344
const char *zSleep = find_option("sleep",0,1);
345
int i;
346
verify_all_options();
347
fossil_print("ProcessID for this process: %lld\n", backofficeProcessId());
348
if( zSleep ) sqlite3_sleep(1000*atoi(zSleep));
349
for(i=2; i<g.argc; i++){
350
sqlite3_uint64 x = (sqlite3_uint64)atoi(g.argv[i]);
351
fossil_print("ProcessId %lld: exists %d done %d\n",
352
x, backofficeProcessExists(x),
353
backofficeProcessDone(x));
354
}
355
}
356
357
/*
358
** COMMAND: test-backoffice-lease
359
**
360
** Usage: %fossil test-backoffice-lease ?--reset?
361
**
362
** Print out information about the backoffice "lease" entry in the
363
** config table that controls whether or not backoffice should run.
364
**
365
** If the --reset option is given, the backoffice lease is reset.
366
** The use of the --reset option can be disruptive. It can cause two
367
** or more backoffice processes to be run simultaneously. Use it with
368
** caution.
369
*/
370
void test_backoffice_lease(void){
371
sqlite3_int64 tmNow = time(0);
372
Lease x;
373
const char *zLease;
374
db_find_and_open_repository(0,0);
375
if( find_option("reset",0,0)!=0 ){
376
db_unprotect(PROTECT_CONFIG);
377
db_multi_exec(
378
"DELETE FROM repository.config WHERE name='backoffice'"
379
);
380
db_protect_pop();
381
}
382
verify_all_options();
383
zLease = db_get("backoffice","");
384
fossil_print("now: %lld\n", tmNow);
385
fossil_print("lease: \"%s\"\n", zLease);
386
backofficeReadLease(&x);
387
fossil_print("idCurrent: %-20lld", x.idCurrent);
388
if( backofficeProcessExists(x.idCurrent) ) fossil_print(" (exists)");
389
if( backofficeProcessDone(x.idCurrent) ) fossil_print(" (done)");
390
fossil_print("\n");
391
fossil_print("tmCurrent: %-20lld", x.tmCurrent);
392
if( x.tmCurrent>0 ){
393
fossil_print(" (now%+d)\n",x.tmCurrent-tmNow);
394
}else{
395
fossil_print("\n");
396
}
397
fossil_print("idNext: %-20lld", x.idNext);
398
if( backofficeProcessExists(x.idNext) ) fossil_print(" (exists)");
399
if( backofficeProcessDone(x.idNext) ) fossil_print(" (done)");
400
fossil_print("\n");
401
fossil_print("tmNext: %-20lld", x.tmNext);
402
if( x.tmNext>0 ){
403
fossil_print(" (now%+d)\n",x.tmNext-tmNow);
404
}else{
405
fossil_print("\n");
406
}
407
}
408
409
/*
410
** If backoffice processing is needed set the backofficeDb variable to the
411
** name of the database file. If no backoffice processing is needed,
412
** this routine makes no changes to state.
413
*/
414
void backoffice_check_if_needed(void){
415
Lease x;
416
sqlite3_uint64 tmNow;
417
418
if( backofficeDb ) return;
419
if( g.zRepositoryName==0 ) return;
420
if( g.db==0 ) return;
421
if( !db_table_exists("repository","config") ) return;
422
if( db_get_boolean("backoffice-disable",0) ) return;
423
tmNow = time(0);
424
backofficeReadLease(&x);
425
if( x.tmNext>=tmNow && backofficeProcessExists(x.idNext) ){
426
/* Another backoffice process is already queued up to run. This
427
** process does not need to do any backoffice work. */
428
return;
429
}else{
430
/* We need to run backup to be (at a minimum) on-deck */
431
backofficeDb = fossil_strdup(g.zRepositoryName);
432
}
433
}
434
435
/*
436
** Call this routine to disable backoffice
437
*/
438
void backoffice_disable(void){
439
backofficeDb = "x";
440
}
441
442
/*
443
** Check for errors prior to running backoffice_thread() or backoffice_run().
444
*/
445
static void backoffice_error_check_one(int *pOnce){
446
if( *pOnce ){
447
fossil_panic("multiple calls to backoffice()");
448
}
449
*pOnce = 1;
450
if( g.db==0 ){
451
fossil_panic("database not open for backoffice processing");
452
}
453
if( db_transaction_nesting_depth()!=0 ){
454
fossil_panic("transaction %s not closed prior to backoffice processing",
455
db_transaction_start_point());
456
}
457
}
458
459
/* This is the main loop for backoffice processing.
460
**
461
** If another process is already working as the current backoffice and
462
** the on-deck backoffice, then this routine returns very quickly
463
** without doing any work.
464
**
465
** If no backoffice processes are running at all, this routine becomes
466
** the main backoffice.
467
**
468
** If a primary backoffice is running, but an on-deck backoffice is
469
** needed, this routine becomes that on-deck backoffice.
470
*/
471
static void backoffice_thread(void){
472
Lease x;
473
sqlite3_uint64 tmNow;
474
sqlite3_uint64 idSelf;
475
int lastWarning = 0;
476
int warningDelay = 30;
477
static int once = 0;
478
479
if( sqlite3_db_readonly(g.db, 0) ) return;
480
if( db_is_protected(PROTECT_READONLY) ) return;
481
g.zPhase = "backoffice-pending";
482
backoffice_error_check_one(&once);
483
idSelf = backofficeProcessId();
484
while(1){
485
tmNow = time(0);
486
db_begin_write();
487
backofficeReadLease(&x);
488
if( x.tmNext>=tmNow
489
&& x.idNext!=idSelf
490
&& backofficeProcessExists(x.idNext)
491
){
492
/* Another backoffice process is already queued up to run. This
493
** process does not need to do any backoffice work and can stop
494
** immediately. */
495
db_end_transaction(0);
496
backofficeTrace("/***** Backoffice Processing Not Needed In %d *****/\n",
497
GETPID());
498
break;
499
}
500
if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){
501
/* This process can start doing backoffice work immediately */
502
x.idCurrent = idSelf;
503
x.tmCurrent = tmNow + BKOFCE_LEASE_TIME;
504
x.idNext = 0;
505
x.tmNext = 0;
506
g.zPhase = "backoffice-work";
507
backofficeWriteLease(&x);
508
db_end_transaction(0);
509
backofficeTrace("/***** Begin Backoffice Processing %d *****/\n",
510
GETPID());
511
backoffice_work();
512
break;
513
}
514
if( backofficeNoDelay || db_get_boolean("backoffice-nodelay",0) ){
515
/* If the no-delay flag is set, exit immediately rather than queuing
516
** up. Assume that some future request will come along and handle any
517
** necessary backoffice work. */
518
db_end_transaction(0);
519
backofficeTrace(
520
"/***** Backoffice No-Delay Exit For %d *****/\n",
521
GETPID());
522
break;
523
}
524
/* This process needs to queue up and wait for the current lease
525
** to expire before continuing. */
526
x.idNext = idSelf;
527
x.tmNext = (tmNow>x.tmCurrent ? tmNow : x.tmCurrent) + BKOFCE_LEASE_TIME;
528
backofficeWriteLease(&x);
529
db_end_transaction(0);
530
backofficeTrace("/***** Backoffice On-deck %d *****/\n", GETPID());
531
if( x.tmCurrent >= tmNow ){
532
if( backofficeSleep(1000*(x.tmCurrent - tmNow + 1)) ){
533
/* The sleep was interrupted by a signal from another thread. */
534
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
535
db_end_transaction(0);
536
break;
537
}
538
}else{
539
if( (sqlite3_uint64)(lastWarning+warningDelay) < tmNow ){
540
sqlite3_int64 runningFor = BKOFCE_LEASE_TIME + tmNow - x.tmCurrent;
541
if( warningDelay>=240 && runningFor<1800 ){
542
fossil_warning(
543
"backoffice process %lld still running after %d seconds",
544
x.idCurrent, runningFor);
545
}
546
lastWarning = tmNow;
547
warningDelay *= 2;
548
}
549
if( backofficeSleep(1000) ){
550
/* The sleep was interrupted by a signal from another thread. */
551
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
552
db_end_transaction(0);
553
break;
554
}
555
}
556
}
557
return;
558
}
559
560
/*
561
** Append to a message to the backoffice log, if the log is open.
562
*/
563
void backoffice_log(const char *zFormat, ...){
564
va_list ap;
565
if( backofficeBlob==0 ) return;
566
blob_append_char(backofficeBlob, ' ');
567
va_start(ap, zFormat);
568
blob_vappendf(backofficeBlob, zFormat, ap);
569
va_end(ap);
570
}
571
572
#if !defined(_WIN32)
573
/*
574
** Capture routine for signals while running backoffice.
575
*/
576
static void backoffice_signal_handler(int sig){
577
const char *zSig = 0;
578
if( sig==SIGSEGV ) zSig = "SIGSEGV";
579
if( sig==SIGFPE ) zSig = "SIGFPE";
580
if( sig==SIGABRT ) zSig = "SIGABRT";
581
if( sig==SIGILL ) zSig = "SIGILL";
582
if( zSig==0 ){
583
backoffice_log("signal-%d", sig);
584
}else{
585
backoffice_log("%s", zSig);
586
}
587
fprintf(backofficeFILE, "%s\n", blob_str(backofficeBlob));
588
fflush(backofficeFILE);
589
exit(1);
590
}
591
#endif
592
593
#if !defined(_WIN32)
594
/*
595
** Convert a struct timeval into an integer number of microseconds
596
*/
597
static long long int tvms(struct timeval *p){
598
return ((long long int)p->tv_sec)*1000000 + (long long int)p->tv_usec;
599
}
600
#endif
601
602
603
/*
604
** This routine runs to do the backoffice processing. When adding new
605
** backoffice processing tasks, add them here.
606
*/
607
void backoffice_work(void){
608
/* Log the backoffice run for testing purposes. For production deployments
609
** the "backoffice-logfile" property should be unset and the following code
610
** should be a no-op. */
611
const char *zLog = backofficeLogfile;
612
Blob log;
613
int nThis;
614
int nTotal = 0;
615
#if !defined(_WIN32)
616
struct timeval sStart, sEnd;
617
#endif
618
if( zLog==0 ) zLog = db_get("backoffice-logfile",0);
619
if( zLog && zLog[0] && (backofficeFILE = fossil_fopen(zLog,"a"))!=0 ){
620
int i;
621
char *zName = db_get("project-name",0);
622
#if !defined(_WIN32)
623
gettimeofday(&sStart, 0);
624
signal(SIGSEGV, backoffice_signal_handler);
625
signal(SIGABRT, backoffice_signal_handler);
626
signal(SIGFPE, backoffice_signal_handler);
627
signal(SIGILL, backoffice_signal_handler);
628
#endif
629
if( zName==0 ){
630
zName = (char*)file_tail(g.zRepositoryName);
631
if( zName==0 ) zName = "(unnamed)";
632
}else{
633
/* Convert all spaces in the "project-name" into dashes */
634
for(i=0; zName[i]; i++){ if( zName[i]==' ' ) zName[i] = '-'; }
635
}
636
blob_init(&log, 0, 0);
637
backofficeBlob = &log;
638
blob_appendf(&log, "%s %s", db_text(0, "SELECT datetime('now')"), zName);
639
}
640
641
/* Here is where the actual work of the backoffice happens */
642
g.zPhase = "backoffice-alerts";
643
nThis = alert_backoffice(0);
644
if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
645
g.zPhase = "backoffice-hooks";
646
nThis = hook_backoffice();
647
if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
648
g.zPhase = "backoffice-close";
649
650
/* Close the log */
651
if( backofficeFILE ){
652
if( nTotal || backofficeLogDetail ){
653
if( nTotal==0 ) backoffice_log("no-op");
654
#if !defined(_WIN32)
655
gettimeofday(&sEnd,0);
656
backoffice_log("elapse-time %d us", tvms(&sEnd) - tvms(&sStart));
657
#endif
658
fprintf(backofficeFILE, "%s\n", blob_str(backofficeBlob));
659
}
660
fclose(backofficeFILE);
661
}
662
}
663
664
/*
665
** COMMAND: backoffice*
666
**
667
** Usage: %fossil backoffice [OPTIONS...] [REPOSITORIES...]
668
**
669
** Run backoffice processing on the repositories listed. If no
670
** repository is specified, run it on the repository of the local check-out.
671
**
672
** This might be done by a cron job or similar to make sure backoffice
673
** processing happens periodically. Or, the --poll option can be used
674
** to run this command as a daemon that will periodically invoke backoffice
675
** on a collection of repositories.
676
**
677
** If only a single repository is named and --poll is omitted, then the
678
** backoffice work is done in-process. But if there are multiple repositories
679
** or if --poll is used, a separate sub-process is started for each poll of
680
** each repository.
681
**
682
** Standard options:
683
**
684
** --debug Show what this command is doing
685
**
686
** --logfile FILE Append a log of backoffice actions onto FILE
687
**
688
** --min N When polling, invoke backoffice at least
689
** once every N seconds even if the repository
690
** never changes. 0 or negative means disable
691
** this feature. Default: 3600 (once per hour).
692
**
693
** --poll N Repeat backoffice calls for repositories that
694
** change in approximately N-second intervals.
695
** N less than 1 turns polling off (the default).
696
** Recommended polling interval: 60 seconds.
697
**
698
** --trace Enable debugging output on stderr
699
**
700
** Options intended for internal use only which may change or be
701
** discontinued in future releases:
702
**
703
** --nodelay Do not queue up or wait for a backoffice job
704
** to complete. If no work is available or if
705
** backoffice has run recently, return immediately.
706
**
707
** --nolease Always run backoffice, even if there is a lease
708
** conflict. This option implies --nodelay. This
709
** option is added to secondary backoffice commands
710
** that are invoked by the --poll option.
711
*/
712
void backoffice_command(void){
713
int nPoll;
714
int nMin;
715
const char *zPoll;
716
int bDebug = 0;
717
int bNoLease = 0;
718
unsigned int nCmd = 0;
719
if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1;
720
if( find_option("nodelay",0,0)!=0 ) backofficeNoDelay = 1;
721
backofficeLogfile = find_option("logfile",0,1);
722
zPoll = find_option("poll",0,1);
723
nPoll = zPoll ? atoi(zPoll) : 0;
724
zPoll = find_option("min",0,1);
725
nMin = zPoll ? atoi(zPoll) : 3600;
726
bDebug = find_option("debug",0,0)!=0;
727
bNoLease = find_option("nolease",0,0)!=0;
728
729
/* Silently consume the -R or --repository flag, leaving behind its
730
** argument. This is for legacy compatibility. Older versions of the
731
** backoffice command only ran on a single repository that was specified
732
** using the -R option. */
733
(void)find_option("repository","R",0);
734
735
verify_all_options();
736
if( g.argc>3 || nPoll>0 ){
737
/* Either there are multiple repositories named on the command-line
738
** or we are polling. In either case, each backoffice should be run
739
** using a separate sub-process */
740
int i;
741
time_t iNow = 0;
742
time_t ix;
743
i64 *aLastRun = fossil_malloc( sizeof(i64)*g.argc );
744
memset(aLastRun, 0, sizeof(i64)*g.argc );
745
while( 1 /* exit via "break;" */){
746
time_t iNext = time(0);
747
for(i=2; i<g.argc; i++){
748
Blob cmd;
749
if( !file_isfile(g.argv[i], ExtFILE) ){
750
continue; /* Repo no longer exists. Ignore it. */
751
}
752
if( iNow
753
&& iNow>file_mtime(g.argv[i], ExtFILE)
754
&& (nMin<=0 || aLastRun[i]+nMin>iNow)
755
){
756
continue; /* Not yet time to run this one */
757
}
758
blob_init(&cmd, 0, 0);
759
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
760
blob_append(&cmd, " backoffice --nodelay", -1);
761
if( g.fAnyTrace ){
762
blob_append(&cmd, " --trace", -1);
763
}
764
if( bDebug ){
765
blob_append(&cmd, " --debug", -1);
766
}
767
if( nPoll>0 ){
768
blob_append(&cmd, " --nolease", -1);
769
}
770
if( backofficeLogfile ){
771
blob_append(&cmd, " --logfile", -1);
772
blob_append_escaped_arg(&cmd, backofficeLogfile, 1);
773
}
774
blob_append_escaped_arg(&cmd, g.argv[i], 1);
775
nCmd++;
776
if( bDebug ){
777
fossil_print("COMMAND[%u]: %s\n", nCmd, blob_str(&cmd));
778
}
779
fossil_system(blob_str(&cmd));
780
aLastRun[i] = iNext;
781
blob_reset(&cmd);
782
}
783
if( nPoll<1 ) break;
784
iNow = iNext;
785
ix = time(0);
786
if( ix < iNow+nPoll ){
787
sqlite3_int64 nMS = (iNow + nPoll - ix)*1000;
788
if( bDebug )fossil_print("SLEEP: %lld\n", nMS);
789
sqlite3_sleep((int)nMS);
790
}
791
}
792
}else{
793
/* Not polling and only one repository named. Backoffice is run
794
** once by this process, which then exits */
795
if( g.argc==3 ){
796
g.zRepositoryOption = g.argv[2];
797
g.argc--;
798
}
799
db_find_and_open_repository(0,0);
800
if( bDebug ){
801
backofficeLogDetail = 1;
802
}
803
if( bNoLease ){
804
backoffice_work();
805
}else{
806
backoffice_thread();
807
}
808
}
809
}
810
811
/*
812
** This is the main interface to backoffice from the rest of the system.
813
** This routine launches either backoffice_thread() directly or as a
814
** subprocess.
815
*/
816
void backoffice_run_if_needed(void){
817
if( backofficeDb==0 ) return;
818
if( strcmp(backofficeDb,"x")==0 ) return;
819
if( g.db ) return;
820
if( g.repositoryOpen ) return;
821
#if defined(_WIN32)
822
{
823
int i;
824
intptr_t x;
825
char *argv[4];
826
wchar_t *ax[5];
827
argv[0] = g.nameOfExe;
828
argv[1] = "backoffice";
829
argv[2] = "-R";
830
argv[3] = backofficeDb;
831
ax[4] = 0;
832
for(i=0; i<=3; i++) ax[i] = fossil_utf8_to_unicode(argv[i]);
833
x = _wspawnv(_P_NOWAIT, ax[0], (const wchar_t * const *)ax);
834
for(i=0; i<=3; i++) fossil_unicode_free(ax[i]);
835
backofficeTrace(
836
"/***** Subprocess %d creates backoffice child %lu *****/\n",
837
GETPID(), GetProcessId((HANDLE)x));
838
if( x>=0 ) return;
839
}
840
#else /* unix */
841
{
842
pid_t pid = fork();
843
if( pid>0 ){
844
/* This is the parent in a successful fork(). Return immediately. */
845
backofficeTrace(
846
"/***** Subprocess %d creates backoffice child %d *****/\n",
847
GETPID(), (int)pid);
848
return;
849
}
850
if( pid==0 ){
851
/* This is the child of a successful fork(). Run backoffice. */
852
int i;
853
setsid();
854
for(i=0; i<=2; i++){
855
close(i);
856
open("/dev/null", O_RDWR);
857
}
858
for(i=3; i<100; i++){ close(i); }
859
g.fDebug = 0;
860
g.httpIn = 0;
861
g.httpOut = 0;
862
db_open_repository(backofficeDb);
863
backofficeDb = "x";
864
backoffice_thread();
865
db_close(1);
866
backofficeTrace("/***** Backoffice Child %d exits *****/\n", GETPID());
867
exit(0);
868
}
869
fossil_warning("backoffice process %d fork failed, errno %d", GETPID(),
870
errno);
871
}
872
#endif
873
/* Fork() failed or is unavailable. Run backoffice in this process, but
874
** do so with the no-delay setting.
875
*/
876
backofficeNoDelay = 1;
877
db_open_repository(backofficeDb);
878
backofficeDb = "x";
879
backoffice_thread();
880
db_close(1);
881
}
882

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button