Fossil SCM

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

Keyboard Shortcuts

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