Skip to content

CUBRID cubrid Admin CLI — Verb Dispatcher, SA/CS-Routed Library Loading, and the Service · Server · Broker · Heartbeat Family

A unified admin CLI is the single binary every database engine ships for non-interactive operations the DBA needs to script: start/stop the server, take a backup, dump statistics, list transactions, kill a runaway query, rebuild a catalog. The design question is how to organize many small operations behind one entry point without the dispatch logic itself becoming the bottleneck on adding new verbs.

Two design choices dominate the space:

  1. Flat vs. nested verb namespace. A flat namespace puts every operation directly under the binary (pg_basebackup, pg_dump, pg_restore, pg_isready — Postgres ships a separate binary per major operation). A nested namespace dispatches by a first argument that names the operation (mysqld --initialize, mongod --repair; or in the more developed form, kubectl get pods, aws s3 ls, cubrid loaddb). Flat is easier for shell completion and man pages; nested concentrates discoverability (cubrid --help lists everything) and shares global option parsing.

  2. Built-in vs. dynamically-loaded verb implementations. Verbs can be compiled directly into the binary (the kubectl model), or each verb can live in a separate shared library and be dlopen-ed only when invoked (the loadable-module model used by some DBA tool families). The dynamic model lets the verb code link against engine libraries without forcing every verb to share the same link mode (some verbs need a database connection, others need to be the database in standalone mode), at the cost of a dlopen per invocation.

CUBRID picks nested verbs + dynamically loaded implementations + a two-axis verb taxonomy:

  • The first axis is the subsystem family (server, broker, heartbeat, manager, pl, gateway, service — “service” is the meta-family that orchestrates all the others).
  • The second axis is the action (start, stop, restart, status, plus a handful of family-specific actions like deregister, reload, acl, reset, info).
  • A separate “admin” axis carries the database operations (createdb, backupdb, loaddb, unloaddb, tranlist, killtran, lockdb, plandump, statdump, paramdump, acldb, …) — these don’t have an “action” suffix; the verb name is the action.

The cross-product of (family × action) is large enough that the code maintains an explicit bitmask compatibility table: each family declares which actions are valid for it, and each action declares which families accept it. A bare cubrid heartbeat reset fails at parse time because reset is MASK_BROKER | MASK_GATEWAY, not MASK_HEARTBEAT.

EngineAdmin CLI shapeVerb dispatchSubprocess management
PostgreSQLMany separate binaries (pg_ctl, pg_basebackup, pg_dump, pg_restore, pg_isready, pgbench)Per-binary main; pg_ctl is the closest analogue to cubrid <action> serverpg_ctl start forks postgres; lifecycle managed via PID file under data/postmaster.pid
MySQLMultiple binaries (mysqladmin, mysqldump, mysqlimport, mysqlcheck, mysql_upgrade); mysqld is the server itselfPer-binary main; mysqladmin is the catch-all command toolsystemd / mysql.server script; per-instance PID files
Oraclesrvctl, crsctl, lsnrctl, expdp, impdp, rman; the meta-tool is srvctl for cluster-wide operationssrvctl <verb> <noun> [args] — verb-noun nested dispatchOHASD / CSSD / CRSD daemons; cluster registry in OCR
MongoDBmongod is the server; mongosh is the shell; mongodump/mongorestore/mongoimport/mongoexport are operation-specific binariesPer-binary mainmongod --fork or systemd unit
CUBRIDOne binary cubrid for both subsystem control and database operationscubrid <family> <action> [args] for service-family verbs; cubrid <verb> [args] for database-admin verbscub_master is the registry / supervisor; broker has its own per-CAS pool; heartbeat is a separate daemon

CUBRID’s “all in one binary” choice descends from the engine’s process model — cub_master is itself a long-lived supervisor for cub_server and cub_pl instances (see cubrid-master-process.md and cubrid-heartbeat.md), and the cubrid binary is mostly a thin client that asks cub_master to do things via a small RPC. Operations that don’t need cub_master (the database-admin verbs like createdb, unloaddb) are dispatched through a separate code path that loads the SA-mode engine library directly.

The cubrid binary’s main lives in util_service.c. It looks at argv[1] and decides between two dispatch arms:

cubrid <argv[1]> [argv[2] ...]
┌────────────────┴────────────────────┐
│ │
argv[1] is a service family argv[1] is a database-admin verb
(service|server|broker|...) (createdb|loaddb|tranlist|...)
│ │
▼ ▼
parse argv[2] as command dispatch via process_admin()
(start|stop|status|...) → util_get_utility_index()
│ → util_get_library_name() (SA or CS)
verify (family, command) → utility_load_library()
bitmask compatibility → utility_load_symbol()
│ → call function with UTIL_FUNCTION_ARG
process_service / process_server /
process_broker / process_heartbeat /
process_manager / process_pl / process_gateway

The detection happens in two parse_arg calls against us_Service_map (~50 entries: 8 family names + 2 meta entries --help / --version + 40 admin verb names all mapped to the sentinel ADMIN). If the first parse succeeds with a family token, the service arm runs; if it succeeds with ADMIN, the admin arm runs; if neither, it’s an error.

Family + command form a 2D grid. The cell at (family, command) is “valid” when (family_mask & command_mask) != 0, where masks come from us_Service_map (for families) and us_Command_map (for commands). The masks are:

// util_service.c — family bitmasks
MASK_SERVICE = 1 << 0
MASK_SERVER = 1 << 1
MASK_BROKER = 1 << 2
MASK_MANAGER = 1 << 3
MASK_HEARTBEAT = 1 << 4
MASK_ADMIN = 1 << 5
MASK_PL = 1 << 6
MASK_GATEWAY = 1 << 7
MASK_ALL = (1 << N) - 1
// us_Command_map (paraphrased — full table in util_service.c:227)
{START, "start", MASK_ALL & ~MASK_PL},
{STOP, "stop", MASK_ALL & ~MASK_PL},
{RESTART, "restart", MASK_SERVICE | MASK_SERVER | MASK_BROKER | MASK_GATEWAY | MASK_PL},
{STATUS, "status", MASK_ALL},
{DEREGISTER, "deregister", MASK_HEARTBEAT},
{LIST, "list", MASK_HEARTBEAT},
{RELOAD, "reload", MASK_HEARTBEAT},
{ON, "on", MASK_BROKER | MASK_GATEWAY},
{OFF, "off", MASK_BROKER | MASK_GATEWAY},
{ACCESS_CONTROL, "acl", MASK_SERVER | MASK_BROKER | MASK_GATEWAY},
{RESET, "reset", MASK_BROKER | MASK_GATEWAY},
{INFO, "info", MASK_BROKER | MASK_GATEWAY},
{SC_COPYLOGDB, "copylogdb", MASK_HEARTBEAT},
{SC_APPLYLOGDB,"applylogdb", MASK_HEARTBEAT},
{GET_SHARID, "getid", MASK_BROKER},
{TEST, "test", MASK_BROKER},
{REPLICATION, "replication"|"repl", MASK_HEARTBEAT},

The process_* family of static functions in util_service.c then takes over. They do the actual work:

FamilyDispatcherWhat it does
serviceprocess_serviceMeta-orchestrator — starts/stops every component listed in cubrid.conf’s service section (SERVICE_START_SERVER, SERVICE_START_BROKER, etc.); used by init scripts
serverprocess_serverForks cub_server per database name; uses is_server_running() + PID-file lookup; can register the server with heartbeat if ha_mode is on
brokerprocess_brokerManages cub_broker and the per-broker CAS pools; acl reload re-reads the broker ACL file; info dumps per-broker stats
managerprocess_managerManages the cubrid_manager HTTP daemon (the web admin tool)
heartbeatprocess_heartbeat (with sub-dispatchers _start/_stop/_deregister/_status/_reload/_util/_replication)HA orchestration — see cubrid-heartbeat.md
plprocess_pl (with _restart/_status sub-dispatchers)Manages cub_pl per database name — see cubrid-pl-javasp.md §“Process topology”
gatewayprocess_gatewayManages cub_gateway for the gateway feature

Most family dispatchers ultimately fork-and-exec the underlying binary (cub_server, cub_broker, cub_master for service-family operations, cub_pl for pl operations) using proc_execute_internal — a small wrapper around fork/execv that optionally redirects stdout/stderr and optionally hides command-line args (for password-bearing args).

The other arm is the database-admin verb dispatcher. It looks up the verb in ua_Utility_Map (defined in util_admin.c:966 — 42 entries) to get a utility_index, then dlopens the right library based on the verb’s mode column (SA_ONLY, CS_ONLY, or SA_CS):

// util_admin.c — UTIL_MAP entry shape
typedef struct {
int utility_index; // CREATEDB, LOADDB, ...
int mode; // SA_ONLY, CS_ONLY, SA_CS
int min_args; // minimum required positional args
const char * util_name; // "createdb", "loaddb", ...
const char * function_name; // entry symbol in the loaded library
GETOPT_LONG *option_table;
UTIL_ARG_MAP *arg_map;
} UTIL_MAP;
// Sample rows (~42 total):
{CREATEDB, SA_ONLY, 2, UTIL_OPTION_CREATEDB, "createdb", ...},
{BACKUPDB, SA_CS, 1, UTIL_OPTION_BACKUPDB, "backupdb", ...},
{LOCKDB, CS_ONLY, 1, UTIL_OPTION_LOCKDB, "lockdb", ...},
{KILLTRAN, CS_ONLY, 1, UTIL_OPTION_KILLTRAN, "killtran", ...},
{LOADDB, SA_CS, 1, UTIL_OPTION_LOADDB, "loaddb_user",...},
{UNLOADDB, SA_CS, 1, UTIL_OPTION_UNLOADDB, "unloaddb", ...},
{COMPACTDB, SA_CS, 1, UTIL_OPTION_COMPACTDB, "compactdb", ...},
{PARAMDUMP, SA_CS, 1, UTIL_OPTION_PARAMDUMP, "paramdump", ...},
{STATDUMP, CS_ONLY, 1, UTIL_OPTION_STATDUMP, "statdump", ...},
{TRANLIST, CS_ONLY, 1, UTIL_OPTION_TRANLIST, "tranlist", ...},
{ACLDB, CS_ONLY, 1, UTIL_OPTION_ACLDB, "acldb", ...},
{CHECKSUMDB, CS_ONLY, 1, UTIL_OPTION_CHECKSUMDB, "checksumdb", ...},
{TDE, SA_CS, 1, UTIL_OPTION_TDE, "tde", ...},
{FLASHBACK, CS_ONLY, 2, UTIL_OPTION_FLASHBACK, "flashback", ...},
{MEMMON, CS_ONLY, 1, UTIL_OPTION_MEMMON, "memmon", ...},

The dispatch code:

// util_admin.c::main (paraphrased)
if (util_get_utility_index (&utility_index, argv[1]) != NO_ERROR)
goto print_usage; // verb not in table
if (util_parse_argument (&ua_Utility_Map[utility_index],
argc - 1, &argv[1]) != NO_ERROR)
is_valid_arg = false; // bad args; still load lib for usage
library_name = util_get_library_name (utility_index);
status = utility_load_library (&library_handle, library_name);
util_get_function_name (&symbol_name, argv[1]);
utility_load_symbol (library_handle, &symbol_handle, symbol_name);
UTIL_FUNCTION_ARG util_func_arg;
util_func_arg.arg_map = ua_Utility_Map[utility_index].arg_map;
util_func_arg.command_name = ua_Utility_Map[utility_index].utility_name;
util_func_arg.argv0 = argv[0];
util_func_arg.argv = argv;
util_func_arg.valid_arg = is_valid_arg;
loaded_function = (UTILITY_FUNCTION) symbol_handle;
status = (*loaded_function) (&util_func_arg);

util_get_library_name returns:

  • libcubridsa for SA_ONLY verbs — createdb, restoredb, optimizedb, installdb, diagdb, patchdb, alterdbhost, genlocale, dumplocale, synccolldb, gen_tz, dump_tz, restoreslave. These verbs are the engine for the duration of the call (the SA library has the entire engine linked in).
  • libcubridcs for CS_ONLY verbs — lockdb, killtran, plandump, statdump, tranlist, changemode, copylogdb, applylogdb, applyinfo, acldb, checksumdb, vacuumdb, flashback, memmon. These verbs talk to a running cub_server over the network.
  • libcubridsa or libcubridcs for SA_CS verbs — backupdb, addvoldb, spacedb, cleanfiledb, checkdb, loaddb, unloaddb, compactdb, paramdump, tde. These verbs accept either; the user picks via -S / --SA-mode or -C / --CS-mode in the verb’s own option table. The library choice is then resolved at parse time before dlopen.

The verb’s entry-function name comes from a fixed transformation of the verb name (e.g., loaddbloaddb_user per the table; most verbs map identically). The util_get_function_name helper does this mapping.

The full set of database-admin verbs:

FamilyVerbs
Database lifecyclecreatedb, deletedb, renamedb, copydb, installdb, restoredb, restoreslave, alterdbhost
Volume / spaceaddvoldb, spacedb, cleanfiledb, diagdb, checkdb, patchdb
Bulk import / exportloaddb, unloaddb, compactdb, backupdb
Catalog statistics & tuningoptimizedb, statdump, paramdump, plandump, vacuumdb, memmon
Transactions & lockslockdb, tranlist, killtran, acldb
HA & replicationchangemode, copylogdb, applylogdb, applyinfo, checksumdb
Recoveryflashback
Encryptiontde
Locale / timezonegenlocale, dumplocale, synccolldb, gen_tz, dump_tz

Each verb has its own dedicated subsection in this knowledge base or its own dedicated doc — see Adjacent reading at the end.

// util_service.c::main (paraphrased)
sprintf (env_buf, "%d", pid);
envvar_set (UTIL_PID_ENVVAR_NAME, env_buf); // children read $CUBRID_UTIL_PID
ER_SAFE_INIT (NULL, ER_NEVER_EXIT); // no-exit error handler
if (argc == 2 && parse_arg (us_Service_map, argv[1]) == UTIL_VERSION) {
util_service_version (argv[0]); // cubrid --version
return EXIT_SUCCESS;
}
util_type = parse_arg (us_Service_map, argv[1]);
if (util_type == ER_GENERIC_ERROR) {
util_type = parse_arg (us_Service_map, argv[2]); // some legacy invocations swap order
if (util_type == ER_GENERIC_ERROR) goto error;
if (util_type == ADMIN) util_name_pos = 2;
} else if (util_type == ADMIN) {
util_name_pos = 1;
}
load_properties (); // read $CUBRID/conf/cubrid.conf
ha_mode_in_common = prm_get_integer_value (PRM_ID_HA_MODE_FOR_SA_UTILS_ONLY);
if (util_type == ADMIN) {
util_log_write_command (argc, argv, util_name_pos);
status = process_admin (argc, argv);
util_log_write_result (status);
return status;
}
if (util_type == UTIL_HELP) goto usage;
if (argc < 3) goto usage;
command_type = parse_arg (us_Command_map, argv[2]);
if (command_type != ER_GENERIC_ERROR) {
int util_mask = util_get_service_option_mask (util_type);
int command_mask = util_get_command_option_mask (command_type);
if ((util_mask & command_mask) == 0) // bitmask cross-check
goto error;
}
util_log_write_command (argc, argv, util_name_pos);
/* dispatch into process_service / process_server / process_broker / ... */

Three observations from the entry flow:

  1. Argv-order tolerance. The dispatcher tries argv[1] first and, on failure, retries argv[2]. This accommodates legacy start cubrid <db> invocations that came from older init scripts; the position of the family token wasn’t fixed.
  2. UTIL_PID_ENVVAR_NAME is set so child processes (forked cub_server, cub_broker, etc.) can correlate themselves back to the spawning cubrid invocation in audit logs.
  3. load_properties() reads cubrid.conf once, populates us_Property_map (database lists for service start, broker list, manager list, heartbeat config, gateway list), and these feed every subsequent process-family operation.

Every admin invocation is logged to $CUBRID/log/cubrid_utility.log via four helpers:

  • util_log_write_command (argc, argv, name_pos) — at start, logs the full command line (with sensitive args masked starting at name_pos).
  • util_log_write_result (status) — at end, logs the exit code.
  • util_log_write_errid (msgid) / util_log_write_errstr (msg) — for in-flight errors.

This is independent of the engine’s er_log and the DDL audit log; it captures the operator’s activity at the binary level — useful for reconstructing what was done when an HA failover misbehaves or a database disappears.

Older CUBRID releases shipped per-operation binaries: createdb, loaddb, unloaddb, compactdb, backupdb, restoredb, addvoldb, spacedb, lockdb, killtran, etc., plus csql and commdb. Modern CUBRID still ships these names but each is a front-end shim built from util_front.c whose main:

  1. Looks at basename(argv[0]) — its own program name.
  2. Finds the matching entry in ua_Util_table[] (a 19-row table pairing program name → modern verb name → arg-translation table).
  3. Builds a new argv of the form ["cubrid", "<modern-verb>", ...translated args...].
  4. Translates each old short-form arg (-p, -c, -l, -mv, …) to the modern long-form (--pages, --comment, --log-path, --more-volumes-file, …) using the per-verb arg map (ua_Create_map, ua_Backup_map, etc.).
  5. execvps into UTIL_ADMIN_NAME (the modern cubrid binary) with the translated args.
// util_front.c::main (paraphrased)
program_name = basename (argv[0]);
if (strcmp (program_name, UTIL_SQLX_NAME) == 0) {
/* csql via legacy name */
convert_argv (argc, &admin_argv[1], ua_Sqlx_map);
execvp (UTIL_CSQL_NAME, admin_argv);
} else if (strcmp (program_name, UTIL_OLD_COMMDB_NAME) == 0) {
/* commdb via legacy name */
convert_argv (argc, &admin_argv[1], us_Commdb_map);
execvp (UTIL_COMMDB_NAME, argv);
} else {
for (i = 0; ua_Util_table[i].app_name != 0; i++) {
if (strcmp (ua_Util_table[i].app_name, program_name) == 0) {
admin_argv[0] = UTIL_CUBRID; // "cubrid"
admin_argv[1] = ua_Util_table[i].util_name; // modern verb
memcpy (&admin_argv[2], &argv[1], (argc - 1) * sizeof (char *));
convert_argv (argc, &admin_argv[2], ua_Util_table[i].match_table);
break;
}
}
execvp (UTIL_ADMIN_NAME, admin_argv);
}

The convert_argv walker is straightforward: look up each argv[i] that starts with - in the per-verb map; if found, replace with the long form; if the long form is the sentinel (char *) -1, drop the arg entirely (used for retired flags); if the arg starts with - but isn’t in the map, fail with “invalid option”; otherwise pass through unchanged.

The shim has two consequences:

  • Old scripts still work. A 2010-era backup script doing loaddb -u dba -p secret -d data.dat mydb runs end-to-end without modification because loaddb is a binary shim that translates and re-execs.
  • The cubrid binary is the only “real” entry. Once the shim execvps, the surviving process’s argv[0] is cubrid. Audit logs see the modern form, not the legacy form. (The shim itself doesn’t log — the unified entry does, via util_log_write_command.)

cubrid commdb (and the legacy bare commdb binary) is a thin client to cub_master for service-list / server-shutdown commands. Its main lives in commdb.c (separate from util_admin.c and util_service.c):

  • cubrid commdb -P — list registered server processes.
  • cubrid commdb -O — list everything cub_master knows about (including broker and pl).
  • cubrid commdb -S <db> — request a shutdown of one server.
  • cubrid commdb -A — request shutdown of every registered server.
  • cubrid commdb -h <host> — target a remote cub_master (rare; most operations are local).

It’s logically part of the cubrid admin family but lives in its own file because it speaks the master-process RPC directly rather than forking a subprocess. Operators rarely call it by name; it’s used by the service-family process_* functions to query / signal master.

cubrid --help and cubrid --version are detected as special entries in us_Service_map (the UTIL_HELP and UTIL_VERSION sentinels). Help renders the family list + brief usage; version forwards to print_admin_version which dlsyms the version symbol out of libcubridsa and calls it. This is one of two places where the binary dlopens a library purely for an informational call (the other is print_admin_usage).

SymbolRole
mainThe cubrid binary’s entry; detects family / admin / help / version, runs the bitmask cross-check, dispatches to family or admin arm
parse_argResolves a string against an option-map array; returns the option type or ER_GENERIC_ERROR
us_Service_mapThe 50-entry table of family + admin-verb tokens
us_Command_mapThe 17-entry table of action commands with their family-acceptance bitmask
us_Property_mapOrdered list of cubrid.conf property keys read by load_properties
load_properties / finalize_properties / get_propertycubrid.conf reader and accessor
process_service / process_server / process_broker / process_manager / process_heartbeat / process_pl / process_gatewayPer-family dispatchers; each takes (command_type, argc, argv, ...)
process_adminThe admin arm — wraps util_admin.c::main-equivalent logic for the case where it’s invoked through cubrid <verb> rather than through the legacy compat shim
proc_execute_internal / proc_execute / proc_execute_hide_cmd_argsfork/execv wrapper with optional output redirect and arg masking
process_masterIssues a request to cub_master (for shutdown all, etc.)
is_server_running / is_broker_running / is_gateway_running / is_manager_running / is_pl_runningPID-file or process-list probes; used by start to detect “already running” and by status to render
util_log_write_command / util_log_write_result / util_log_write_errid / util_log_write_errstrcubrid_utility.log writers
print_message / print_result / command_stringHelp and end-of-run messaging

Verb table and SA/CS dispatch (util_admin.c)

Section titled “Verb table and SA/CS dispatch (util_admin.c)”
SymbolRole
mainStandalone admin binary entry (used by the legacy compat shim’s execvp target); same dispatch as process_admin in util_service.c
ua_Utility_MapThe 42-entry table of admin verbs with mode/argv-min/option-table
util_get_utility_indexVerb name → table index
util_get_library_nameVerb’s mode → library name (libcubridsa or libcubridcs)
util_get_function_nameVerb name → entry symbol in the loaded library
util_parse_argumentPer-verb argv parser using the table’s GETOPT_LONG and UTIL_ARG_MAP columns
print_admin_usage / print_admin_versionForward to dlsym-ed libcubridsa symbols for usage/version text

Per-verb option / arg-map types (util_common.c, util_admin.c)

Section titled “Per-verb option / arg-map types (util_common.c, util_admin.c)”

Each verb declares a paired GETOPT_LONG ua_<Verb>_Option[] (the getopt short→long table) and a UTIL_ARG_MAP ua_<Verb>_Option_Map[] (the argv parse-result schema: positional args + per-flag value type). Both are static arrays in util_admin.c. The shape:

// pattern repeated per verb in util_admin.c
static GETOPT_LONG ua_Backup_Option[] = {
{BACKUP_DESTINATION_PATH_L, 1, 0, BACKUP_DESTINATION_PATH_S},
{BACKUP_REMOVE_ARCHIVE_L, 0, 0, BACKUP_REMOVE_ARCHIVE_S},
/* ... */
{0, 0, 0, 0}
};
static UTIL_ARG_MAP ua_Backup_Option_Map[] = {
{OPTION_STRING_TABLE, {0}, {0}}, // positional args
{BACKUP_DESTINATION_PATH_S, {ARG_STRING}, {0}},
{BACKUP_REMOVE_ARCHIVE_S, {ARG_BOOLEAN}, {0}},
/* ... */
{0, {0}, {0}}
};

The table-driven approach means adding a new admin verb is a purely declarative change — declare two arrays, add a row to ua_Utility_Map, and write the verb function in either libcubridsa or libcubridcs (or both for SA_CS).

SymbolRole
mainDetects program name; picks the matching arg-translation table; rewrites argv; execvps into cubrid (or csql / commdb)
ua_Util_tableProgram-name → modern-verb-name + per-verb arg map (19 rows)
ua_Create_map / ua_Rename_map / ua_Copy_map / …Per-verb short→long arg translations (18 tables, one per legacy binary)
get_long_arg_by_old_argSingle-arg lookup
convert_argvWalks argv, replaces each entry via lookup, drops sentinel -1-marked args, errors on unknown -x
SymbolRole
mainParses -P/-O/-S/-A/-h; opens connection to cub_master; sends one of MASTER_CONN_LIST_* / MASTER_REQ_SHUTDOWN_* requests; renders or applies the response
process_master_* (in util_service.c)Higher-level wrappers that let the family dispatchers issue master requests without forking commdb
SymbolPath
util_service.c::mainsrc/executables/util_service.c:534
us_Service_mapsrc/executables/util_service.c:152
us_Command_mapsrc/executables/util_service.c:227
us_Property_mapsrc/executables/util_service.c:249
Family MASK_* constants and UTIL_TYPE_* stringssrc/executables/util_service.c:143
process_admin invocation sitesrc/executables/util_service.c:599
util_admin.c::mainsrc/executables/util_admin.c:1093
ua_Utility_Mapsrc/executables/util_admin.c:966
util_get_utility_index (declaration)src/executables/util_admin.c:1015
util_get_library_name / util_get_function_name (declarations)src/executables/util_admin.c:1013–1014
util_front.c::mainsrc/executables/util_front.c:369
ua_Util_table (legacy program-name → modern-verb)src/executables/util_front.c:278
convert_argvsrc/executables/util_front.c:333

Symbol names are the canonical anchor; line numbers are hints scoped to the updated: date.

  • Two mains, one binary. Both util_service.c and util_admin.c define main. The CMake build links util_service.c::main as the cubrid binary’s entry; the util_admin.c::main is built into a separate cub_admin binary used by the legacy util_front.c shim’s execvp target. The two share ua_Utility_Map (declared in util_admin.c, referenced from util_service.c::process_admin) so verb resolution is consistent whichever binary the user actually hit.
  • SA_CS resolution timing. Verbs flagged SA_CS defer the library choice until after util_parse_argument, because that parse is what discovers whether the user passed -S (SA mode) or -C (CS mode). This means the parse error path runs against the same library load logic as success — ensuring usage messages come from the right library even when args were rejected.
  • is_valid_arg = false is forwarded to the verb function. The verb is loaded and called even if its args parsed badly, so the verb can render its own usage. The valid_arg field tells it to print usage and exit rather than attempting work.
  • load_properties is not idempotent across reload. It reads cubrid.conf once at startup. A cubrid service start followed by a manual cubrid.conf edit and a cubrid service status doesn’t see the edits — operators have to restart the cubrid invocation. The heartbeat-only reload action is the exception; it re-reads ha-config independently.
  • MASK_PL is excluded from start / stop. PL is started / stopped indirectly by the per-database server (cub_server forks cub_pl); operators interact with it via restart or status only. Trying cubrid pl start <db> fails the bitmask check.
  • The legacy shim does not log. util_front.c::main does no util_log_write_*; the audit log is populated by the modern cubrid binary after the execvp. This means a script using legacy binaries appears in the audit log only as the modern verb invocation, with no record that the entry was the legacy name.
  • commdb dual-path. commdb exists both as a standalone binary and as cubrid commdb; both speak directly to cub_master. The process_master helpers in util_service.c are an in-process bypass of the commdb binary, used so that family dispatchers don’t need to fork.
  • Per-verb help granularity. cubrid <verb> --help is delegated to the loaded library’s usage function (different per-verb formatting). A unified cubrid --help <verb> form that pulls from a common message catalog is undocumented and may not exist in current message files.
  • cubrid_utility.log rotation. The log is append-only; no documented rotation policy. Long-running clusters with frequent service operations accumulate the log indefinitely.
  • Bitmask collision audit. The (family, action) cross-check table is hand-maintained; a missing mask bit silently rejects a valid combination. There’s no test that walks every (family, action) pair to confirm the mask sets match documented behaviour.
  • Legacy program-name list staleness. ua_Util_table is fixed at 19 rows. New admin verbs added to ua_Utility_Map (memmon, flashback, tde) have no legacy short-name entry — operators must use the cubrid <verb> form. Whether this is intentional or a rolling retirement of the shim is undocumented.
  • cubrid commdb deprecation. With process_master available in-process, the standalone commdb binary is mostly redundant. Keeping it shipped is a backward-compat stance; the timeline for retirement isn’t recorded.
  • src/executables/util_service.c — the cubrid binary’s main, family dispatcher, bitmask cross-check, properties loader, per-family process_* functions
  • src/executables/util_admin.c — admin-verb table (ua_Utility_Map), per-verb option tables, library-name and function-name helpers, the standalone cub_admin main
  • src/executables/util_common.c — shared verb helpers, message printing, UTIL_FUNCTION_ARG definitions
  • src/executables/util_cs.c — CS-mode verb implementations (the symbols dlsym-ed when library_name == libcubridcs)
  • src/executables/util_sa.c — SA-mode verb implementations
  • src/executables/util_front.c — legacy compatibility shim; per-verb arg-translation tables; execvp into the modern entry
  • src/executables/util_support.cutility_load_library, utility_load_symbol, utility_load_print_error, message catalog wrappers
  • src/executables/commdb.ccub_master probe binary
  • src/executables/AGENTS.md — agent guide; binary→source map
  • Adjacent docs: cubrid-csql.md (the other major user-facing binary; uses the same SA/CS launcher pattern via csql_launcher.c), cubrid-loaddb.md / cubrid-compactdb.md / cubrid-backup-restore.md (verbs that already have dedicated docs), cubrid-master-process.md (open — see cubrid-coverage.md) for cub_master itself, cubrid-heartbeat.md (heartbeat family lifecycle), cubrid-broker.md (broker family lifecycle), cubrid-pl-javasp.md (pl family lifecycle), cubrid-system-parameters.md (the cubrid.conf properties consumed by load_properties)