mplay.c revision 1.1 1 1.1 christos /* $NetBSD: mplay.c,v 1.1 2025/09/05 21:09:33 christos Exp $ */
2 1.1 christos
3 1.1 christos /* mplay.c - memory-mapped database log replay */
4 1.1 christos /*
5 1.1 christos * Copyright 2011-2023 Howard Chu, Symas Corp.
6 1.1 christos * All rights reserved.
7 1.1 christos *
8 1.1 christos * Redistribution and use in source and binary forms, with or without
9 1.1 christos * modification, are permitted only as authorized by the OpenLDAP
10 1.1 christos * Public License.
11 1.1 christos *
12 1.1 christos * A copy of this license is available in the file LICENSE in the
13 1.1 christos * top-level directory of the distribution or, alternatively, at
14 1.1 christos * <http://www.OpenLDAP.org/license.html>.
15 1.1 christos */
16 1.1 christos #include <stdio.h>
17 1.1 christos #include <stdlib.h>
18 1.1 christos #include <unistd.h>
19 1.1 christos #include <time.h>
20 1.1 christos #include <string.h>
21 1.1 christos #include <ctype.h>
22 1.1 christos #include <assert.h>
23 1.1 christos #include <sys/types.h>
24 1.1 christos #include <sys/wait.h>
25 1.1 christos
26 1.1 christos #include "lmdb.h"
27 1.1 christos
28 1.1 christos #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
29 1.1 christos #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
30 1.1 christos #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
31 1.1 christos "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
32 1.1 christos
33 1.1 christos #define MDB_SCNy(t) "z" #t
34 1.1 christos
35 1.1 christos #define SCMP(s) s, (sizeof(s)-1)
36 1.1 christos char inbuf[8192];
37 1.1 christos char *dbuf, *kbuf;
38 1.1 christos size_t dbufsize;
39 1.1 christos int maxkey;
40 1.1 christos
41 1.1 christos #define SOFF(s) (sizeof(s)+1)
42 1.1 christos
43 1.1 christos #define MAXENVS 16
44 1.1 christos #define MAXTXNS 16
45 1.1 christos #define MAXCRSS 16
46 1.1 christos
47 1.1 christos #define MAXPIDS 16
48 1.1 christos
49 1.1 christos typedef struct crspair {
50 1.1 christos void *tcrs; /* scanned text pointer */
51 1.1 christos MDB_cursor *rcrs;
52 1.1 christos } crspair;
53 1.1 christos
54 1.1 christos typedef struct txnpair {
55 1.1 christos void *ttxn; /* scanned text pointer */
56 1.1 christos MDB_txn *rtxn;
57 1.1 christos crspair cursors[MAXCRSS];
58 1.1 christos int ncursors;
59 1.1 christos } txnpair;
60 1.1 christos
61 1.1 christos typedef struct envpair {
62 1.1 christos void *tenv;
63 1.1 christos MDB_env *renv;
64 1.1 christos txnpair txns[MAXTXNS];
65 1.1 christos int ntxns;
66 1.1 christos } envpair;
67 1.1 christos
68 1.1 christos envpair envs[MAXENVS];
69 1.1 christos int nenvs;
70 1.1 christos
71 1.1 christos envpair *lastenv;
72 1.1 christos txnpair *lasttxn;
73 1.1 christos crspair *lastcrs;
74 1.1 christos
75 1.1 christos typedef struct pidpair {
76 1.1 christos int tpid;
77 1.1 christos pid_t rpid;
78 1.1 christos int fdout, fdin;
79 1.1 christos } pidpair;
80 1.1 christos
81 1.1 christos pidpair *lastpid;
82 1.1 christos
83 1.1 christos pidpair pids[MAXPIDS];
84 1.1 christos int npids;
85 1.1 christos
86 1.1 christos unsigned long lcount;
87 1.1 christos
88 1.1 christos static int unhex(unsigned char *c2)
89 1.1 christos {
90 1.1 christos int x, c;
91 1.1 christos x = *c2++ & 0x4f;
92 1.1 christos if (x & 0x40)
93 1.1 christos x -= 55;
94 1.1 christos c = x << 4;
95 1.1 christos x = *c2 & 0x4f;
96 1.1 christos if (x & 0x40)
97 1.1 christos x -= 55;
98 1.1 christos c |= x;
99 1.1 christos return c;
100 1.1 christos }
101 1.1 christos
102 1.1 christos int inhex(char *in, char *out)
103 1.1 christos {
104 1.1 christos char *c2 = out;
105 1.1 christos while (isxdigit(*in)) {
106 1.1 christos *c2++ = unhex((unsigned char *)in);
107 1.1 christos in += 2;
108 1.1 christos }
109 1.1 christos return c2 - out;
110 1.1 christos }
111 1.1 christos
112 1.1 christos static void addenv(void *tenv, MDB_env *renv)
113 1.1 christos {
114 1.1 christos assert(nenvs < MAXENVS);
115 1.1 christos envs[nenvs].tenv = tenv;
116 1.1 christos envs[nenvs].renv = renv;
117 1.1 christos envs[nenvs].ntxns = 0;
118 1.1 christos lastenv = envs+nenvs;
119 1.1 christos nenvs++;
120 1.1 christos }
121 1.1 christos
122 1.1 christos static envpair *findenv(void *tenv)
123 1.1 christos {
124 1.1 christos int i;
125 1.1 christos if (!lastenv || lastenv->tenv != tenv) {
126 1.1 christos for (i=0; i<nenvs; i++)
127 1.1 christos if (envs[i].tenv == tenv)
128 1.1 christos break;
129 1.1 christos assert(i < nenvs);
130 1.1 christos lastenv = &envs[i];
131 1.1 christos }
132 1.1 christos return lastenv;
133 1.1 christos }
134 1.1 christos
135 1.1 christos static void delenv(envpair *ep)
136 1.1 christos {
137 1.1 christos int i = ep - envs;
138 1.1 christos for (; i<nenvs-1; i++)
139 1.1 christos envs[i] = envs[i+1];
140 1.1 christos nenvs--;
141 1.1 christos lastenv = NULL;
142 1.1 christos }
143 1.1 christos
144 1.1 christos static void addtxn(void *tenv, void *ttxn, MDB_txn *rtxn)
145 1.1 christos {
146 1.1 christos envpair *ep;
147 1.1 christos txnpair *tp;
148 1.1 christos
149 1.1 christos ep = findenv(tenv);
150 1.1 christos assert(ep->ntxns < MAXTXNS);
151 1.1 christos tp = ep->txns+ep->ntxns;
152 1.1 christos tp->ttxn = ttxn;
153 1.1 christos tp->rtxn = rtxn;
154 1.1 christos tp->ncursors = 0;
155 1.1 christos ep->ntxns++;
156 1.1 christos lasttxn = tp;
157 1.1 christos }
158 1.1 christos
159 1.1 christos static txnpair *findtxn(void *ttxn)
160 1.1 christos {
161 1.1 christos int i, j;
162 1.1 christos if (lasttxn && lasttxn->ttxn == ttxn)
163 1.1 christos return lasttxn;
164 1.1 christos if (lastenv) {
165 1.1 christos for (i=0; i<lastenv->ntxns; i++) {
166 1.1 christos if (lastenv->txns[i].ttxn == ttxn) {
167 1.1 christos lasttxn = lastenv->txns+i;
168 1.1 christos return lasttxn;
169 1.1 christos }
170 1.1 christos }
171 1.1 christos }
172 1.1 christos for (i=0; i<nenvs; i++) {
173 1.1 christos if (envs+i == lastenv) continue;
174 1.1 christos for (j=0; j<envs[i].ntxns; j++) {
175 1.1 christos if (envs[i].txns[j].ttxn == ttxn) {
176 1.1 christos lastenv = envs+i;
177 1.1 christos lasttxn = envs[i].txns+j;
178 1.1 christos return lasttxn;
179 1.1 christos }
180 1.1 christos }
181 1.1 christos }
182 1.1 christos assert(0); /* should have found it */
183 1.1 christos }
184 1.1 christos
185 1.1 christos static void deltxn(txnpair *tp)
186 1.1 christos {
187 1.1 christos int i = tp - lastenv->txns;
188 1.1 christos for (; i<lastenv->ntxns-1; i++)
189 1.1 christos lastenv->txns[i] = lastenv->txns[i+1];
190 1.1 christos lastenv->ntxns--;
191 1.1 christos lasttxn = NULL;
192 1.1 christos }
193 1.1 christos
194 1.1 christos static void addcrs(txnpair *tp, void *tcrs, MDB_cursor *rcrs)
195 1.1 christos {
196 1.1 christos int j = tp->ncursors;
197 1.1 christos assert(tp->ncursors < MAXCRSS);
198 1.1 christos
199 1.1 christos tp->cursors[j].tcrs = tcrs;
200 1.1 christos tp->cursors[j].rcrs = rcrs;
201 1.1 christos tp->ncursors++;
202 1.1 christos lastcrs = tp->cursors+j;
203 1.1 christos }
204 1.1 christos
205 1.1 christos static crspair *findcrs(void *tcrs)
206 1.1 christos {
207 1.1 christos int i, j, k;
208 1.1 christos envpair *ep;
209 1.1 christos txnpair *tp;
210 1.1 christos crspair *cp;
211 1.1 christos if (lastcrs && lastcrs->tcrs == tcrs)
212 1.1 christos return lastcrs;
213 1.1 christos if (lasttxn) {
214 1.1 christos for (k=0, cp=lasttxn->cursors; k<lasttxn->ncursors; k++, cp++) {
215 1.1 christos if (cp->tcrs == tcrs) {
216 1.1 christos lastcrs = cp;
217 1.1 christos return lastcrs;
218 1.1 christos }
219 1.1 christos }
220 1.1 christos }
221 1.1 christos if (lastenv) {
222 1.1 christos for (j=0, tp=lastenv->txns; j<lastenv->ntxns; j++, tp++){
223 1.1 christos if (tp == lasttxn)
224 1.1 christos continue;
225 1.1 christos for (k=0, cp = tp->cursors; k<tp->ncursors; k++, cp++) {
226 1.1 christos if (cp->tcrs == tcrs) {
227 1.1 christos lastcrs = cp;
228 1.1 christos lasttxn = tp;
229 1.1 christos return lastcrs;
230 1.1 christos }
231 1.1 christos }
232 1.1 christos }
233 1.1 christos }
234 1.1 christos for (i=0, ep=envs; i<nenvs; i++, ep++) {
235 1.1 christos for (j=0, tp=ep->txns; j<ep->ntxns; j++, tp++) {
236 1.1 christos if (tp == lasttxn)
237 1.1 christos continue;
238 1.1 christos for (k=0, cp = tp->cursors; k<tp->ncursors; k++, cp++) {
239 1.1 christos if (cp->tcrs == tcrs) {
240 1.1 christos lastcrs = cp;
241 1.1 christos lasttxn = tp;
242 1.1 christos lastenv = ep;
243 1.1 christos return lastcrs;
244 1.1 christos }
245 1.1 christos }
246 1.1 christos }
247 1.1 christos }
248 1.1 christos assert(0); /* should have found it already */
249 1.1 christos }
250 1.1 christos
251 1.1 christos static void delcrs(void *tcrs)
252 1.1 christos {
253 1.1 christos int i;
254 1.1 christos crspair *cp = findcrs(tcrs);
255 1.1 christos mdb_cursor_close(cp->rcrs);
256 1.1 christos for (i = cp - lasttxn->cursors; i<lasttxn->ncursors-1; i++)
257 1.1 christos lasttxn->cursors[i] = lasttxn->cursors[i+1];
258 1.1 christos lasttxn->ncursors--;
259 1.1 christos lastcrs = NULL;
260 1.1 christos }
261 1.1 christos
262 1.1 christos void child()
263 1.1 christos {
264 1.1 christos int rc;
265 1.1 christos MDB_val key, data;
266 1.1 christos char *ptr;
267 1.1 christos
268 1.1 christos while (fgets(inbuf, sizeof(inbuf), stdin)) {
269 1.1 christos ptr = inbuf;
270 1.1 christos if (!strncmp(ptr, SCMP("exit")))
271 1.1 christos break;
272 1.1 christos
273 1.1 christos if (!strncmp(ptr, SCMP("mdb_env_create"))) {
274 1.1 christos void *tenv;
275 1.1 christos MDB_env *renv;
276 1.1 christos sscanf(ptr+SOFF("mdb_env_create"), "%p", &tenv);
277 1.1 christos E(mdb_env_create(&renv));
278 1.1 christos addenv(tenv, renv);
279 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_env_set_maxdbs"))) {
280 1.1 christos void *tenv;
281 1.1 christos envpair *ep;
282 1.1 christos unsigned int maxdbs;
283 1.1 christos sscanf(ptr+SOFF("mdb_env_set_maxdbs"), "%p, %u", &tenv, &maxdbs);
284 1.1 christos ep = findenv(tenv);
285 1.1 christos E(mdb_env_set_maxdbs(ep->renv, maxdbs));
286 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_env_set_mapsize"))) {
287 1.1 christos void *tenv;
288 1.1 christos envpair *ep;
289 1.1 christos size_t mapsize;
290 1.1 christos sscanf(ptr+SOFF("mdb_env_set_mapsize"), "%p, %"MDB_SCNy(u), &tenv, &mapsize);
291 1.1 christos ep = findenv(tenv);
292 1.1 christos E(mdb_env_set_mapsize(ep->renv, mapsize));
293 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_env_open"))) {
294 1.1 christos void *tenv;
295 1.1 christos envpair *ep;
296 1.1 christos char *path;
297 1.1 christos int len;
298 1.1 christos unsigned int flags, mode;
299 1.1 christos sscanf(ptr+SOFF("mdb_env_open"), "%p, %n", &tenv, &len);
300 1.1 christos path = ptr+SOFF("mdb_env_open")+len;
301 1.1 christos ptr = strchr(path, ',');
302 1.1 christos *ptr++ = '\0';
303 1.1 christos sscanf(ptr, "%u, %o", &flags, &mode);
304 1.1 christos ep = findenv(tenv);
305 1.1 christos E(mdb_env_open(ep->renv, path, flags, mode));
306 1.1 christos if (!maxkey) {
307 1.1 christos maxkey = mdb_env_get_maxkeysize(ep->renv);
308 1.1 christos kbuf = malloc(maxkey+2);
309 1.1 christos dbuf = malloc(maxkey+2);
310 1.1 christos dbufsize = maxkey;
311 1.1 christos }
312 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_env_close"))) {
313 1.1 christos void *tenv;
314 1.1 christos envpair *ep;
315 1.1 christos sscanf(ptr+SOFF("mdb_env_close"), "%p", &tenv);
316 1.1 christos ep = findenv(tenv);
317 1.1 christos mdb_env_close(ep->renv);
318 1.1 christos delenv(ep);
319 1.1 christos if (!nenvs) /* if no other envs left, this process is done */
320 1.1 christos break;
321 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_txn_begin"))) {
322 1.1 christos unsigned int flags;
323 1.1 christos void *tenv, *ttxn;
324 1.1 christos envpair *ep;
325 1.1 christos MDB_txn *rtxn;
326 1.1 christos sscanf(ptr+SOFF("mdb_txn_begin"), "%p, %*p, %u = %p", &tenv, &flags, &ttxn);
327 1.1 christos ep = findenv(tenv);
328 1.1 christos E(mdb_txn_begin(ep->renv, NULL, flags, &rtxn));
329 1.1 christos addtxn(tenv, ttxn, rtxn);
330 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_txn_commit"))) {
331 1.1 christos void *ttxn;
332 1.1 christos txnpair *tp;
333 1.1 christos sscanf(ptr+SOFF("mdb_txn_commit"), "%p", &ttxn);
334 1.1 christos tp = findtxn(ttxn);
335 1.1 christos E(mdb_txn_commit(tp->rtxn));
336 1.1 christos deltxn(tp);
337 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_txn_abort"))) {
338 1.1 christos void *ttxn;
339 1.1 christos txnpair *tp;
340 1.1 christos sscanf(ptr+SOFF("mdb_txn_abort"), "%p", &ttxn);
341 1.1 christos tp = findtxn(ttxn);
342 1.1 christos mdb_txn_abort(tp->rtxn);
343 1.1 christos deltxn(tp);
344 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_dbi_open"))) {
345 1.1 christos void *ttxn;
346 1.1 christos txnpair *tp;
347 1.1 christos char *dbname;
348 1.1 christos unsigned int flags;
349 1.1 christos unsigned int tdbi;
350 1.1 christos MDB_dbi dbi;
351 1.1 christos sscanf(ptr+SOFF("mdb_dbi_open"), "%p, ", &ttxn);
352 1.1 christos dbname = strchr(ptr+SOFF("mdb_dbi_open"), ',');
353 1.1 christos dbname += 2;
354 1.1 christos ptr = strchr(dbname, ',');
355 1.1 christos *ptr++ = '\0';
356 1.1 christos if (!strcmp(dbname, "(null)"))
357 1.1 christos dbname = NULL;
358 1.1 christos sscanf(ptr, "%u = %u", &flags, &tdbi);
359 1.1 christos tp = findtxn(ttxn);
360 1.1 christos E(mdb_dbi_open(tp->rtxn, dbname, flags, &dbi));
361 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_dbi_close"))) {
362 1.1 christos void *tenv;
363 1.1 christos envpair *ep;
364 1.1 christos unsigned int tdbi;
365 1.1 christos sscanf(ptr+SOFF("mdb_dbi_close"), "%p, %u", &tenv, &tdbi);
366 1.1 christos ep = findenv(tenv);
367 1.1 christos mdb_dbi_close(ep->renv, tdbi);
368 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_cursor_open"))) {
369 1.1 christos void *ttxn, *tcrs;
370 1.1 christos txnpair *tp;
371 1.1 christos MDB_cursor *rcrs;
372 1.1 christos unsigned int tdbi;
373 1.1 christos sscanf(ptr+SOFF("mdb_cursor_open"), "%p, %u = %p", &ttxn, &tdbi, &tcrs);
374 1.1 christos tp = findtxn(ttxn);
375 1.1 christos E(mdb_cursor_open(tp->rtxn, tdbi, &rcrs));
376 1.1 christos addcrs(tp, tcrs, rcrs);
377 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_cursor_close"))) {
378 1.1 christos void *tcrs;
379 1.1 christos sscanf(ptr+SOFF("mdb_cursor_close"), "%p", &tcrs);
380 1.1 christos delcrs(tcrs);
381 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_cursor_put"))) {
382 1.1 christos void *tcrs;
383 1.1 christos crspair *cp;
384 1.1 christos unsigned int flags;
385 1.1 christos int len;
386 1.1 christos sscanf(ptr+SOFF("mdb_cursor_put"), "%p, ", &tcrs);
387 1.1 christos cp = findcrs(tcrs);
388 1.1 christos ptr = strchr(ptr+SOFF("mdb_cursor_put"), ',');
389 1.1 christos sscanf(ptr+1, "%"MDB_SCNy(u)",", &key.mv_size);
390 1.1 christos if (key.mv_size) {
391 1.1 christos ptr = strchr(ptr, '[');
392 1.1 christos inhex(ptr+1, kbuf);
393 1.1 christos key.mv_data = kbuf;
394 1.1 christos ptr += key.mv_size * 2 + 2;
395 1.1 christos }
396 1.1 christos ptr = strchr(ptr+1, ',');
397 1.1 christos sscanf(ptr+1, "%"MDB_SCNy(u)"%n", &data.mv_size, &len);
398 1.1 christos if (data.mv_size > dbufsize) {
399 1.1 christos dbuf = realloc(dbuf, data.mv_size+2);
400 1.1 christos assert(dbuf != NULL);
401 1.1 christos dbufsize = data.mv_size;
402 1.1 christos }
403 1.1 christos ptr += len+1;
404 1.1 christos if (*ptr == '[') {
405 1.1 christos inhex(ptr+1, dbuf);
406 1.1 christos data.mv_data = dbuf;
407 1.1 christos ptr += data.mv_size * 2 + 2;
408 1.1 christos } else {
409 1.1 christos sprintf(dbuf, "%09ld", (long)mdb_txn_id(lasttxn->rtxn));
410 1.1 christos }
411 1.1 christos sscanf(ptr+1, "%u", &flags);
412 1.1 christos E(mdb_cursor_put(cp->rcrs, &key, &data, flags));
413 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_cursor_del"))) {
414 1.1 christos void *tcrs;
415 1.1 christos crspair *cp;
416 1.1 christos unsigned int flags;
417 1.1 christos sscanf(ptr+SOFF("mdb_cursor_del"), "%p, %u", &tcrs, &flags);
418 1.1 christos cp = findcrs(tcrs);
419 1.1 christos E(mdb_cursor_del(cp->rcrs, flags));
420 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_put"))) {
421 1.1 christos void *ttxn;
422 1.1 christos txnpair *tp;
423 1.1 christos unsigned int tdbi, flags;
424 1.1 christos int len;
425 1.1 christos sscanf(ptr+SOFF("mdb_put"),"%p, %u, %"MDB_SCNy(u), &ttxn, &tdbi, &key.mv_size);
426 1.1 christos tp = findtxn(ttxn);
427 1.1 christos ptr = strchr(ptr+SOFF("mdb_put"), '[');
428 1.1 christos inhex(ptr+1, kbuf);
429 1.1 christos key.mv_data = kbuf;
430 1.1 christos ptr += key.mv_size * 2 + 2;
431 1.1 christos sscanf(ptr+1, "%"MDB_SCNy(u)"%n", &data.mv_size, &len);
432 1.1 christos if (data.mv_size > dbufsize) {
433 1.1 christos dbuf = realloc(dbuf, data.mv_size+2);
434 1.1 christos assert(dbuf != NULL);
435 1.1 christos dbufsize = data.mv_size;
436 1.1 christos }
437 1.1 christos ptr += len+1;
438 1.1 christos if (*ptr == '[') {
439 1.1 christos inhex(ptr+1, dbuf);
440 1.1 christos ptr += data.mv_size * 2 + 2;
441 1.1 christos } else {
442 1.1 christos sprintf(dbuf, "%09ld", (long)mdb_txn_id(tp->rtxn));
443 1.1 christos }
444 1.1 christos data.mv_data = dbuf;
445 1.1 christos sscanf(ptr+1, "%u", &flags);
446 1.1 christos RES(MDB_KEYEXIST,mdb_put(tp->rtxn, tdbi, &key, &data, flags));
447 1.1 christos } else if (!strncmp(ptr, SCMP("mdb_del"))) {
448 1.1 christos void *ttxn;
449 1.1 christos txnpair *tp;
450 1.1 christos unsigned int tdbi;
451 1.1 christos int len;
452 1.1 christos sscanf(ptr+SOFF("mdb_del"),"%p, %u, %"MDB_SCNy(u), &ttxn, &tdbi, &key.mv_size);
453 1.1 christos tp = findtxn(ttxn);
454 1.1 christos ptr = strchr(ptr+SOFF("mdb_del"), '[');
455 1.1 christos inhex(ptr+1, kbuf);
456 1.1 christos key.mv_data = kbuf;
457 1.1 christos ptr += key.mv_size * 2 + 2;
458 1.1 christos sscanf(ptr+1, "%"MDB_SCNy(u)"%n", &data.mv_size, &len);
459 1.1 christos if (data.mv_size > dbufsize) {
460 1.1 christos dbuf = realloc(dbuf, data.mv_size+2);
461 1.1 christos assert(dbuf != NULL);
462 1.1 christos dbufsize = data.mv_size;
463 1.1 christos }
464 1.1 christos ptr += len+1;
465 1.1 christos if (*ptr == '[') {
466 1.1 christos inhex(ptr+1, dbuf);
467 1.1 christos } else {
468 1.1 christos sprintf(dbuf, "%09ld", (long)mdb_txn_id(tp->rtxn));
469 1.1 christos }
470 1.1 christos data.mv_data = dbuf;
471 1.1 christos RES(MDB_NOTFOUND,mdb_del(tp->rtxn, tdbi, &key, &data));
472 1.1 christos }
473 1.1 christos write(1, "\n", 1);
474 1.1 christos }
475 1.1 christos exit(0);
476 1.1 christos }
477 1.1 christos
478 1.1 christos static pidpair *addpid(int tpid)
479 1.1 christos {
480 1.1 christos int fdout[2], fdin[2];
481 1.1 christos pid_t pid;
482 1.1 christos assert(npids < MAXPIDS);
483 1.1 christos pids[npids].tpid = tpid;
484 1.1 christos pipe(fdout);
485 1.1 christos pipe(fdin);
486 1.1 christos if ((pid = fork()) == 0) {
487 1.1 christos /* child */
488 1.1 christos fclose(stdin);
489 1.1 christos fclose(stdout);
490 1.1 christos dup2(fdout[0], 0);
491 1.1 christos dup2(fdin[1], 1);
492 1.1 christos stdin = fdopen(0, "r");
493 1.1 christos stdout = fdopen(1, "w");
494 1.1 christos child();
495 1.1 christos return 0; /* NOTREACHED */
496 1.1 christos } else {
497 1.1 christos pids[npids].rpid = pid;
498 1.1 christos pids[npids].fdout = fdout[1];
499 1.1 christos pids[npids].fdin = fdin[0];
500 1.1 christos lastpid = pids+npids;
501 1.1 christos npids++;
502 1.1 christos return lastpid;
503 1.1 christos }
504 1.1 christos }
505 1.1 christos
506 1.1 christos static pidpair *findpid(int tpid)
507 1.1 christos {
508 1.1 christos int i;
509 1.1 christos if (!lastpid || lastpid->tpid != tpid) {
510 1.1 christos for (i=0; i<npids; i++)
511 1.1 christos if (pids[i].tpid == tpid)
512 1.1 christos break;
513 1.1 christos if (i == npids)
514 1.1 christos return NULL;
515 1.1 christos lastpid = &pids[i];
516 1.1 christos }
517 1.1 christos return lastpid;
518 1.1 christos }
519 1.1 christos
520 1.1 christos volatile pid_t killpid;
521 1.1 christos
522 1.1 christos static void delpid(int tpid)
523 1.1 christos {
524 1.1 christos pidpair *pp = findpid(tpid);
525 1.1 christos if (pp) {
526 1.1 christos pid_t kpid = pp->rpid;
527 1.1 christos killpid = kpid;
528 1.1 christos write(pp->fdout, "exit\n", sizeof("exit"));
529 1.1 christos while (killpid == kpid)
530 1.1 christos usleep(10000);
531 1.1 christos }
532 1.1 christos }
533 1.1 christos
534 1.1 christos static void reaper(int sig)
535 1.1 christos {
536 1.1 christos int status, i;
537 1.1 christos pid_t pid = waitpid(-1, &status, 0);
538 1.1 christos if (pid > 0) {
539 1.1 christos fprintf(stderr, "# %s %d\n", WIFEXITED(status) ? "exited" : "killed", pid);
540 1.1 christos for (i=0; i<npids; i++)
541 1.1 christos if (pids[i].rpid == pid)
542 1.1 christos break;
543 1.1 christos assert(i < npids);
544 1.1 christos close(pids[i].fdout);
545 1.1 christos close(pids[i].fdin);
546 1.1 christos for (;i<npids-1; i++)
547 1.1 christos pids[i] = pids[i+1];
548 1.1 christos npids--;
549 1.1 christos killpid = 0;
550 1.1 christos }
551 1.1 christos }
552 1.1 christos
553 1.1 christos int main(int argc,char * argv[])
554 1.1 christos {
555 1.1 christos signal(SIGCHLD, reaper);
556 1.1 christos
557 1.1 christos while (fgets(inbuf, sizeof(inbuf), stdin)) {
558 1.1 christos pidpair *pp;
559 1.1 christos int tpid, len;
560 1.1 christos char c, *ptr;
561 1.1 christos lcount++;
562 1.1 christos
563 1.1 christos if (inbuf[0] == '#' && !strncmp(inbuf+1, SCMP(" killed"))) {
564 1.1 christos sscanf(inbuf+SOFF("killed"),"%d", &tpid);
565 1.1 christos delpid(tpid);
566 1.1 christos continue;
567 1.1 christos }
568 1.1 christos
569 1.1 christos if (inbuf[0] != '>')
570 1.1 christos continue;
571 1.1 christos ptr = inbuf+1;
572 1.1 christos sscanf(ptr, "%d:%n", &tpid, &len);
573 1.1 christos pp = findpid(tpid);
574 1.1 christos if (!pp)
575 1.1 christos pp = addpid(tpid); /* new process */
576 1.1 christos
577 1.1 christos ptr = inbuf+len+1;
578 1.1 christos len = strlen(ptr);
579 1.1 christos write(pp->fdout, ptr, len); /* send command and wait for ack */
580 1.1 christos read(pp->fdin, &c, 1);
581 1.1 christos }
582 1.1 christos while (npids)
583 1.1 christos delpid(pids[0].tpid);
584 1.1 christos }
585