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