1 1.1 christos /* $NetBSD: mplay.c,v 1.2 2025/09/05 21:16:22 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