/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. */ #include "config.h" #ifndef lint static const char sccsid[] = "@(#)tcl_lock.c 10.10 (Sleepycat) 4/10/98"; #endif /* not lint */ /* * This file is divided up into 4 sets of functions: * 1. The lock command and its support functions. * 2. The lock_unlink command. * 3. The lock manager widget commands. * 4. The lock widget commands. */ #ifndef NO_SYSTEM_INCLUDES #include #include #include #include #endif #include #include "db_int.h" #include "dbtest.h" #include "test_ext.h" /* Internal functions */ static int do_lockvec __P((Tcl_Interp *, DB_LOCKTAB *, int, char **)); #ifdef DEBUG void __lock_dump_region __P((DB_LOCKTAB *, unsigned long)); #endif typedef struct _mgr_data { DB_LOCKTAB *tabp; DB_ENV *env; } mgr_data; typedef struct _lock_data { DB_LOCKTAB *tabp; DB_LOCK lock; } lock_data; /* * lock_cmd -- * Implements lock_open for dbtest. Lock_open creates a lock * manager and all the necessary files in the file system. It then * creates a command that implements the other lock functions. */ #define LOCKMGR_USAGE "lock_open path flags mode [options]\n\toptions:\n" int lockmgr_cmd(notused, interp, argc, argv) ClientData notused; Tcl_Interp *interp; int argc; char *argv[]; { static int mgr_number = 0; DB_LOCKTAB *mgrp; DB_ENV *env; mgr_data *md; u_int32_t flags; int mode, ret, tclint; char mgrname[50]; notused = NULL; debug_check(); /* Check number of arguments. */ USAGE_GE(argc, 4, LOCKMGR_USAGE, DO_ENV); if (Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK || Tcl_GetInt(interp, argv[3], &mode) != TCL_OK) return (TCL_ERROR); flags = (u_int32_t)tclint; /* * Call lock_open. */ if (process_env_options(interp, argc, argv, &env)) { Tcl_PosixError(interp); Tcl_SetResult(interp, "NULL", TCL_STATIC); return (TCL_OK); } if (F_ISSET(env, DB_ENV_STANDALONE)) mgrp = env->lk_info; else if ((ret = lock_open(argv[1], flags, mode, env, &mgrp)) != 0) { errno = ret; Tcl_PosixError(interp); Tcl_SetResult(interp, "NULL", TCL_STATIC); return (TCL_OK); } else env->lk_info = mgrp; /* Create new command name. */ if ((md = (mgr_data *)malloc(sizeof(mgr_data))) == NULL) { if (!F_ISSET(env, DB_ENV_STANDALONE)) { (void)db_appexit(env); if (env->lk_conflicts) free(env->lk_conflicts); free(env); } Tcl_SetResult(interp, "lock_open: ", TCL_STATIC); errno = ENOMEM; Tcl_AppendResult(interp, Tcl_PosixError(interp), 0); return (TCL_ERROR); } md->tabp = mgrp; md->env = env; sprintf(&mgrname[0], "lockmgr%d", mgr_number); mgr_number++; /* Create widget command. */ Tcl_CreateCommand(interp, mgrname, lockwidget_cmd, (int *)md, NULL); Tcl_SetResult(interp, mgrname, TCL_VOLATILE); return (TCL_OK); } /* * lockunlink_cmd -- * Implements lock_unlink for dbtest. */ #define LOCKUNLINK_USAGE "lock_unlink path force" int lockunlink_cmd(notused, interp, argc, argv) ClientData notused; Tcl_Interp *interp; int argc; char *argv[]; { int force; notused = NULL; debug_check(); USAGE_GE(argc, 3, LOCKUNLINK_USAGE, 0); if (Tcl_GetInt(interp, argv[2], &force) != TCL_OK) return (TCL_ERROR); if (lock_unlink(argv[1], force, NULL) != 0) { Tcl_SetResult(interp, "-1", TCL_STATIC); return (TCL_OK); } Tcl_SetResult(interp, "0", TCL_STATIC); return (TCL_OK); } /* * lockwidget -- * This is that command that implements the lock widget. If we * ever add new "methods" we add new widget commands here. */ #define LOCKWIDGET_USAGE "lockmgrN option ?arg arg ...?" #define LOCKCLOSE_USAGE "lockmgrN close" #define LOCKDUMP_USAGE "lockmgrN dump flags" #define LOCKGET_USAGE "lockmgrN get locker obj mode flags" #define LOCKVEC_USAGE \ "lockmgrN vec locker flags ?{obj mode op} {obj mode op}...?" /* Not yet implemented. */ int lockwidget_cmd(cd_mgr, interp, argc, argv) ClientData cd_mgr; Tcl_Interp *interp; int argc; char *argv[]; { static int id = 0; DB_LOCKTAB *mgr; DB_LOCK lock; DBT obj_dbt; DB_ENV *env; db_lockmode_t mode; lock_data *ld; u_int32_t locker, flags; int ret, tclint; char lockname[128]; debug_check(); mgr = ((mgr_data *)cd_mgr)->tabp; USAGE_GE(argc, 2, LOCKWIDGET_USAGE, 0); if (strcmp(argv[1], "close") == 0) { USAGE(argc, 2, LOCKCLOSE_USAGE, 0); env = ((mgr_data *)cd_mgr)->env; if (!F_ISSET(env, DB_ENV_STANDALONE)) { (void)db_appexit(env); if (env->lk_conflicts) free(env->lk_conflicts); free(env); } Tcl_DeleteCommand(interp, argv[0]); Tcl_SetResult(interp, "0", TCL_STATIC); return (TCL_OK); } else if (strcmp(argv[1], "get") == 0) { USAGE(argc, 6, LOCKGET_USAGE, 0); if (Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK) { Tcl_PosixError(interp); return (TCL_ERROR); } locker = (u_int32_t)tclint; obj_dbt.data = argv[3]; obj_dbt.size = strlen(argv[3]) + 1; if (Tcl_GetInt(interp, argv[4], &tclint) != TCL_OK) return (TCL_ERROR); mode = tclint; if (Tcl_GetInt(interp, argv[5], &tclint) != TCL_OK) return (TCL_ERROR); flags = (u_int32_t)tclint; ret = lock_get(mgr, locker, flags, &obj_dbt, mode, &lock); switch (ret) { case 0: /* Success */ sprintf(&lockname[0], "%s.lock%d", argv[0], id); id++; if ((ld = (lock_data *)malloc(sizeof(lock_data))) == NULL) { Tcl_PosixError(interp); return (TCL_OK); } ld->tabp = mgr; ld->lock = lock; Tcl_CreateCommand(interp, lockname, lock_cmd, (int *)ld, NULL); Tcl_SetResult(interp, lockname, TCL_VOLATILE); break; case DB_LOCK_NOTGRANTED: Tcl_SetResult(interp, "BLOCKED", TCL_STATIC); return (TCL_OK); case DB_LOCK_DEADLOCK: Tcl_SetResult(interp, "DEADLOCK", TCL_STATIC); return (TCL_OK); default: Tcl_PosixError(interp); return (TCL_ERROR); } } else if (strcmp(argv[1], "vec") == 0) { USAGE(argc, 5, LOCKVEC_USAGE, 0); return (do_lockvec(interp, mgr, argc, argv)); #ifdef DEBUG } else if (strcmp(argv[1], "dump") == 0) { USAGE(argc, 3, LOCKDUMP_USAGE, 0); if (Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK) flags = 0xffff; else flags = (u_int32_t)tclint; __lock_dump_region(mgr, flags); return (TCL_OK); #endif } else { Tcl_SetResult(interp, LOCKWIDGET_USAGE, TCL_STATIC); return (TCL_ERROR); } return (TCL_OK); } #define LOCK_USAGE "lockmgrN.lockM cmd ?arg arg ...?" #define LOCKPUT_USAGE "lockmgrN.lockM put" int lock_cmd(cd_lock, interp, argc, argv) ClientData cd_lock; Tcl_Interp *interp; int argc; char *argv[]; { DB_LOCK lock; DB_LOCKTAB *tab; lock_data *ld; int ret; debug_check(); USAGE_GE(argc, 2, LOCK_USAGE, 0); ld = (lock_data *)cd_lock; lock = ld->lock; tab = ld->tabp; free(ld); if (strcmp(argv[1], "put") == 0) { USAGE(argc, 2, LOCKPUT_USAGE, 0); Tcl_DeleteCommand(interp, argv[0]); if ((ret = lock_put(tab, lock)) != 0) { errno = ret; Tcl_PosixError(interp); return (TCL_OK); } } else { Tcl_SetResult(interp, LOCK_USAGE, TCL_STATIC); return (TCL_ERROR); } Tcl_SetResult(interp, "0", TCL_STATIC); return (TCL_OK); } static int do_lockvec(interp, mgr, argc, argv) Tcl_Interp *interp; DB_LOCKTAB *mgr; int argc; char **argv; { DB_LOCKREQ *reqlist, *err; DBT *dblist; db_lockmode_t mode; db_lockop_t iop; u_int32_t flags, locker; int i, ntup, nreqs, ret, tclint; char **ap, **tuplist; /* Get the locker id and the flags. */ if (Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK) return (TCL_ERROR); locker = (u_int32_t)tclint; if (Tcl_GetInt(interp, argv[3], &tclint) != TCL_OK) return (TCL_ERROR); flags = (u_int32_t)tclint; /* The first three args are command, the rest are tuples. */ nreqs = argc - 4; if ((reqlist = (DB_LOCKREQ *)calloc(sizeof(DB_LOCKREQ), nreqs)) == NULL || (dblist = (DBT *)calloc(sizeof(DBT), nreqs)) == NULL) { Tcl_PosixError(interp); return (TCL_ERROR); } for (i = 0; i < nreqs; i++) reqlist[i].obj = &dblist[i]; for (ap = &argv[4], i = 0; i < nreqs; i++) { tuplist = NULL; if (Tcl_SplitList(interp, *ap, &ntup, &tuplist) != TCL_OK) break; if (Tcl_GetInt(interp, tuplist[1], &tclint) != TCL_OK) break; mode = (db_lockmode_t)tclint; if (Tcl_GetInt(interp, tuplist[2], &tclint) != TCL_OK) break; iop = (db_lockop_t)tclint; reqlist[i].op = iop; reqlist[i].obj->data = (char *)strdup(tuplist[0]); reqlist[i].obj->size = strlen(tuplist[0]); reqlist[i].mode = mode; free(tuplist); } if (tuplist == NULL || i != nreqs) ret = TCL_ERROR; else if ((ret = lock_vec(mgr, locker, flags, reqlist, nreqs, &err)) != 0) { Tcl_SetResult(interp, "lock_vec failed, returned ", TCL_STATIC); switch (ret) { case DB_LOCK_DEADLOCK: Tcl_AppendResult(interp, "DEADLOCK", 0); break; case DB_LOCK_NOTHELD: Tcl_AppendResult(interp, "NOTHELD", 0); break; case DB_LOCK_NOTGRANTED: Tcl_AppendResult(interp, "NOTGRANTED", 0); break; default: errno = ret; Tcl_AppendResult(interp, Tcl_PosixError(interp), 0); break; } Tcl_AppendResult(interp, " on request ", argv[4 + (err - reqlist)], NULL); ret = TCL_ERROR; } else { Tcl_SetResult(interp, "0", TCL_STATIC); ret = TCL_OK; } for (i = 0; i < nreqs; i++) if (reqlist[i].obj->data != NULL) free(reqlist[i].obj->data); free(reqlist); free(dblist); return (ret); }