콘텐츠로 이동

(KO) CUBRID cubrid Admin CLI — Verb 디스패처, SA/CS 라우팅 라이브러리 로딩, Service · Server · Broker · Heartbeat 패밀리

통합 admin CLI 는 데이터베이스 엔진이 DBA 가 스크립팅해야 하는 비-인터랙티브 작업을 위해 ship 하는 단일 바이너리이다 — 서버 시작/중지, 백업, 통계 dump, 트랜잭션 list, runaway 쿼리 kill, 카탈로그 재구축. 디자인 질문은 많은 작은 동작을 하나의 진입점 뒤에 어떻게 조직해서 디스패치 로직 자체가 새 verb 를 추가하는 데 병목이 되지 않게 하느냐이다.

두 가지 디자인 선택이 공간을 지배한다는 점이다:

  1. 평탄 vs 중첩 verb 네임스페이스. 평탄 네임스페이스는 모든 동작을 바이너리 직속에 둔다 (pg_basebackup, pg_dump, pg_restore, pg_isready — Postgres 는 주요 동작당 별도 바이너리를 ship 한다). 중첩 네임스페이스는 동작을 명명하는 첫 인자로 디스패치한다 (mysqld --initialize, mongod --repair; 더 발달한 형식으로는 kubectl get pods, aws s3 ls, cubrid loaddb). 평탄은 shell completion 과 man 페이지에 더 쉽고, 중첩은 발견 가능성을 모은다는 점이다 (cubrid --help 가 모든 것을 list).

  2. 빌트인 vs 동적 로드 verb 구현. verb 들이 바이너리에 직접 컴파일되거나 (kubectl 모델), 또는 각 verb 가 별도 공유 라이브러리에 살고 호출될 때만 dlopen 된다 (일부 DBA tool 패밀리가 쓰는 loadable-module 모델). 동적 모델은 모든 verb 가 같은 link 모드를 공유하도록 강제하지 않고 verb 코드가 엔진 라이브러리에 링크하게 해준다는 점이다 (어떤 verb 는 데이터 베이스 연결이 필요하고, 다른 것은 standalone 모드에서 데이터 베이스 자체가 되어야 한다). 대가는 호출당 dlopen 이다.

CUBRID 는 중첩 verb + 동적 로드 구현 + 두 축 verb 분류 체계 를 고른다는 점이다:

  • 첫 축은 서브시스템 패밀리 (server, broker, heartbeat, manager, pl, gateway, service — service 는 다른 모든 것을 조율하는 메타 패밀리).
  • 둘째 축은 동작 (start, stop, restart, status, 그리고 몇 패밀리별 동작 deregister, reload, acl, reset, info).
  • 별도의 admin 축이 데이터베이스 동작 (createdb, backupdb, loaddb, unloaddb, tranlist, killtran, lockdb, plandump, statdump, paramdump, acldb, …)을 운반한다 — 이들은 동작 접미사가 없다. verb 이름 자체가 동작이다.

(패밀리 × 동작) 의 cross-product 가 충분히 커서 코드는 명시적 비트마스크 호환 표 를 유지한다는 점이다 — 각 패밀리는 어떤 동작이 자기에게 유효한지를 선언하고, 각 동작은 어떤 패밀리가 자기를 받아들이는지를 선언한다. 그냥 cubrid heartbeat reset 은 파싱 시점에 실패한다 — resetMASK_BROKER | MASK_GATEWAY 이고 MASK_HEARTBEAT 가 아니기 때문이다.

엔진Admin CLI 모양Verb 디스패치Subprocess 관리
PostgreSQL많은 별도 바이너리 (pg_ctl, pg_basebackup, pg_dump, pg_restore, pg_isready, pgbench)per-binary main; pg_ctlcubrid <action> server 의 가장 가까운 유사물pg_ctl startpostgres 를 fork; lifecycle 은 data/postmaster.pid 의 PID 파일
MySQL다중 바이너리 (mysqladmin, mysqldump, mysqlimport, mysqlcheck, mysql_upgrade); mysqld 가 서버 자체per-binary main; mysqladmin 이 catch-all command toolsystemd / mysql.server 스크립트; per-instance PID 파일
Oraclesrvctl, crsctl, lsnrctl, expdp, impdp, rman; cluster 동작에 대한 메타 도구는 srvctlsrvctl <verb> <noun> [args] — verb-noun 중첩 디스패치OHASD / CSSD / CRSD 데몬; cluster 레지스트리는 OCR
MongoDBmongod 는 서버, mongosh 는 shell, mongodump/mongorestore/mongoimport/mongoexport 는 동작별 바이너리per-binary mainmongod --fork 또는 systemd unit
CUBRID서브시스템 제어와 데이터베이스 동작 모두를 단일 바이너리 cubrid서비스 패밀리 verb 에는 cubrid <family> <action> [args]; 데이터베이스 admin verb 에는 cubrid <verb> [args]cub_master 가 레지스트리 / supervisor; broker 는 자기 per-CAS 풀; heartbeat 는 별도 데몬

CUBRID 의 all in one binary 선택은 엔진의 프로세스 모델에서 내려왔다는 점이다 — cub_master 자체가 cub_servercub_pl 인스턴스에 대한 long-lived supervisor 이고 (cubrid-master-process.mdcubrid-heartbeat.md 참조), cubrid 바이너리는 대부분 작은 RPC 로 cub_master 에게 일을 부탁하는 thin client 이다. cub_master 가 필요 없는 동작 (createdb, unloaddb 같은 데이터베이스-admin verb)은 SA-mode 엔진 라이브러리를 직접 로드하는 별도 코드 경로로 디스패치된다는 뜻이다.

cubrid 바이너리의 mainutil_service.c 에 산다는 점이다. argv[1] 을 보고 두 디스패치 arm 사이에서 결정한다:

cubrid <argv[1]> [argv[2] ...]
┌────────────────┴────────────────────┐
│ │
argv[1] 이 서비스 패밀리 argv[1] 이 데이터베이스-admin verb
(service|server|broker|...) (createdb|loaddb|tranlist|...)
│ │
▼ ▼
argv[2] 를 명령으로 파싱 process_admin() 으로 디스패치
(start|stop|status|...) → util_get_utility_index()
│ → util_get_library_name() (SA or CS)
(family, command) 비트마스크 호환 → utility_load_library()
검증 → utility_load_symbol()
│ → UTIL_FUNCTION_ARG 와 함께 함수 호출
process_service / process_server /
process_broker / process_heartbeat /
process_manager / process_pl / process_gateway

감지는 us_Service_map (~50 항목: 8 패밀리 이름 + 2 메타 항목 --help / --version + 40 admin verb 이름이 모두 sentinel ADMIN 으로 매핑)에 대한 두 parse_arg 호출에서 일어난다는 점이다. 첫 파싱이 패밀리 토큰으로 성공하면 service arm 이 돌고, ADMIN 으로 성공하면 admin arm 이 돌고, 둘 다 아니면 에러이다.

패밀리 + 명령이 2D grid 를 이룬다. (패밀리, 명령) 의 cell 이 (family_mask & command_mask) != 0 일 때 유효 이다 (마스크는 패밀리에 대해서는 us_Service_map, 명령에 대해서는 us_Command_map 에서). 마스크는:

// util_service.c — 패밀리 비트마스크
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 (의역 — 전체 표는 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},

util_service.cprocess_* 패밀리 정적 함수들이 그 다음에 실제 작업을 한다는 뜻이다:

패밀리디스패처무엇을 하는가
serviceprocess_service메타 orchestrator — cubrid.confservice 절에 나열된 모든 컴포넌트 (SERVICE_START_SERVER, SERVICE_START_BROKER 등)를 시작/중지; init 스크립트가 사용
serverprocess_server데이터베이스 이름당 cub_server 를 fork; is_server_running() + PID-파일 lookup 사용; ha_mode 가 켜져 있으면 서버를 heartbeat 에 등록 가능
brokerprocess_brokercub_broker 와 broker 별 CAS 풀 관리; acl reload 가 broker ACL 파일 재읽기; info 가 broker 별 통계 dump
managerprocess_managercubrid_manager HTTP 데몬 (web admin 도구) 관리
heartbeatprocess_heartbeat (sub-dispatcher 들 _start/_stop/_deregister/_status/_reload/_util/_replication 와 함께)HA orchestration — cubrid-heartbeat.md 참조
plprocess_pl (sub-dispatcher 들 _restart/_status 와 함께)데이터베이스 이름당 cub_pl 관리 — cubrid-pl-javasp.md §Process topology 참조
gatewayprocess_gatewaygateway 기능을 위한 cub_gateway 관리

대부분의 패밀리 디스패처는 결국 proc_execute_internal — optionally stdout/stderr 를 redirect 하고 optionally command-line args 를 숨기는 (패스워드 운반 args 를 위해) fork/execv 를 감싼 작은 wrapper — 를 써서 underlying 바이너리 (서비스-패밀리 동작에는 cub_server, cub_broker, cub_master, pl 동작에는 cub_pl)를 fork-and-exec 한다.

데이터베이스-admin arm: process_admin

섹션 제목: “데이터베이스-admin arm: process_admin”

다른 arm 은 데이터베이스-admin verb 디스패처이다. ua_Utility_Map (util_admin.c:966 에 정의 — 42 항목)에서 verb 를 찾아 utility_index 를 얻은 다음, verb 의 mode 컬럼 (SA_ONLY, CS_ONLY, SA_CS)에 따라 올바른 라이브러리를 dlopen 한다는 점이다:

// util_admin.c — UTIL_MAP 항목 모양
typedef struct {
int utility_index; // CREATEDB, LOADDB, ...
int mode; // SA_ONLY, CS_ONLY, SA_CS
int min_args; // 최소 위치 인자 수
const char * util_name; // "createdb", "loaddb", ...
const char * function_name; // 로드된 라이브러리의 진입 심볼
GETOPT_LONG *option_table;
UTIL_ARG_MAP *arg_map;
} UTIL_MAP;
// 샘플 행 (~42 총):
{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", ...},

디스패치 코드:

// util_admin.c::main (의역)
if (util_get_utility_index (&utility_index, argv[1]) != NO_ERROR)
goto print_usage; // verb 가 표에 없음
if (util_parse_argument (&ua_Utility_Map[utility_index],
argc - 1, &argv[1]) != NO_ERROR)
is_valid_arg = false; // 잘못된 arg; 그래도 usage 위해 lib 로드
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 이 돌려주는 것:

  • SA_ONLY verb 에는 libcubridsacreatedb, restoredb, optimizedb, installdb, diagdb, patchdb, alterdbhost, genlocale, dumplocale, synccolldb, gen_tz, dump_tz, restoreslave. 이 verb 들은 호출 동안 엔진이 된다 (SA 라이브러리는 엔진 전체를 링크해두고 있다).
  • CS_ONLY verb 에는 libcubridcslockdb, killtran, plandump, statdump, tranlist, changemode, copylogdb, applylogdb, applyinfo, acldb, checksumdb, vacuumdb, flashback, memmon. 이 verb 들은 동작 중인 cub_server 와 네트워크로 대화한다.
  • SA_CS verb 에는 libcubridsa 또는 libcubridcsbackupdb, addvoldb, spacedb, cleanfiledb, checkdb, loaddb, unloaddb, compactdb, paramdump, tde. 사용자가 verb 자기 옵션 표에서 -S / --SA-mode 또는 -C / --CS-mode 로 고른다. 라이브러리 선택은 dlopen 전에 파싱 시점에 해석 된다.

verb 의 진입 함수 이름은 verb 이름의 고정 변환에서 온다 (예: loaddbloaddb_user per the table; 대부분의 verb 는 동일 매핑). util_get_function_name 도우미가 이 매핑을 한다.

데이터베이스-admin verb 의 전체 집합:

패밀리Verb
데이터베이스 lifecyclecreatedb, deletedb, renamedb, copydb, installdb, restoredb, restoreslave, alterdbhost
볼륨 / 공간addvoldb, spacedb, cleanfiledb, diagdb, checkdb, patchdb
벌크 import / exportloaddb, unloaddb, compactdb, backupdb
카탈로그 통계 & 튜닝optimizedb, statdump, paramdump, plandump, vacuumdb, memmon
트랜잭션 & 락lockdb, tranlist, killtran, acldb
HA & 복제changemode, copylogdb, applylogdb, applyinfo, checksumdb
복구flashback
암호화tde
로케일 / 타임존genlocale, dumplocale, synccolldb, gen_tz, dump_tz

각 verb 가 이 지식 베이스 안에 자기 전용 절을 가지거나 자기 전용 문서를 가진다 — 끝의 인접 읽을거리 참조.

// util_service.c::main (의역)
sprintf (env_buf, "%d", pid);
envvar_set (UTIL_PID_ENVVAR_NAME, env_buf); // 자식들이 $CUBRID_UTIL_PID 를 읽음
ER_SAFE_INIT (NULL, ER_NEVER_EXIT); // no-exit 에러 핸들러
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]); // 일부 레거시 호출은 순서가 뒤바뀜
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 (); // $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) // 비트마스크 cross-check
goto error;
}
util_log_write_command (argc, argv, util_name_pos);
/* process_service / process_server / process_broker / ... 로 디스패치 */

진입 흐름에서의 세 가지 관찰:

  1. Argv 순서 관용. 디스패처가 먼저 argv[1] 을 시도하고 실패하면 argv[2] 를 다시 시도한다. 패밀리 토큰의 위치가 고정되지 않았던 옛 init 스크립트의 start cubrid <db> 호출 을 수용하기 위함이다.
  2. UTIL_PID_ENVVAR_NAME 가 설정되어, 자식 프로세스 (forked cub_server, cub_broker 등)가 audit 로그에서 자기를 발신 cubrid 호출과 상관시킬 수 있게 한다.
  3. load_properties()cubrid.conf 를 한 번 읽어 us_Property_map (서비스 시작용 데이터베이스 list, broker list, manager list, heartbeat config, gateway list)을 채우고, 이들이 이어지는 모든 process-family 동작에 먹여진다.

모든 admin 호출이 네 가지 도우미로 $CUBRID/log/cubrid_utility.log 에 로깅된다는 점이다:

  • util_log_write_command (argc, argv, name_pos) — 시작에, 전체 command line 을 로깅 (name_pos 부터 민감한 args 를 마스킹).
  • util_log_write_result (status) — 끝에, exit 코드 로깅.
  • util_log_write_errid (msgid) / util_log_write_errstr (msg) — flight 중 에러용.

이는 엔진의 er_log 와 DDL audit 로그와는 독립적이다 — 운영자 의 활동을 바이너리 수준에서 캡처한다 — HA failover 가 잘못 동작했거나 데이터베이스가 사라졌을 때 무엇을 했는지 재구성하는 데 유용하다.

이전 CUBRID 릴리스는 동작당 바이너리를 ship 했다 — createdb, loaddb, unloaddb, compactdb, backupdb, restoredb, addvoldb, spacedb, lockdb, killtran 등 거기에 csqlcommdb 도. 모던 CUBRID 도 여전히 이 이름들을 ship 하지만 각각 이 util_front.c 에서 빌드된 front-end shim 이다 — 그 main 은:

  1. basename(argv[0]) — 자기 프로그램 이름 — 을 본다.
  2. ua_Util_table[] (프로그램 이름 → 모던 verb 이름 → arg-번역 표를 짝지은 19 줄 표)에서 매칭 항목을 찾는다.
  3. [cubrid, "<modern-verb>", ...번역된 args...] 형태의 새 argv 를 구축한다.
  4. 각 옛 짧은 형식 arg (-p, -c, -l, -mv, …)를 verb 별 arg map (ua_Create_map, ua_Backup_map 등)을 사용해서 모던 long 형식 (--pages, --comment, --log-path, --more-volumes-file, …)로 번역한다.
  5. 번역된 args 와 함께 UTIL_ADMIN_NAME (모던 cubrid 바이너리) 으로 execvp 한다.
// util_front.c::main (의역)
program_name = basename (argv[0]);
if (strcmp (program_name, UTIL_SQLX_NAME) == 0) {
/* 레거시 이름의 csql */
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 */
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; // 모던 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);
}

convert_argv walker 는 단순하다 — - 로 시작하는 각 argv[i] 를 verb 별 map 에서 찾는다. 발견되면 long 형식으로 교체. long 형식이 sentinel (char *) -1 이면 arg 를 통째로 떨군다 (은퇴된 플래그용). - 로 시작하지만 map 에 없으면 invalid option 으로 실패. 그 외에는 변경 없이 통과시킨다는 점이다.

shim 은 두 가지 결과를 가진다:

  • 옛 스크립트가 여전히 작동한다. loaddb -u dba -p secret -d data.dat mydb 를 하는 2010 년대 백업 스크립트가 변경 없이 end-to-end 로 동작한다 — loaddb 가 번역하고 re-exec 하는 바이너리 shim 이기 때문이다.
  • cubrid 바이너리가 유일한 진짜 진입점이다. shim 이 execvp 한 순간부터, 살아남은 프로세스의 argv[0]cubrid 이다. Audit 로그는 모던 형식을 본다 — 레거시 형식이 아니다. (shim 자체는 로깅하지 않고 — 통합 진입점이 util_log_write_command 로 로깅한다.)

cubrid commdb (그리고 레거시 bare commdb 바이너리)는 서비스-list / 서버-shutdown 명령을 위해 cub_master 에 대한 thin client 이다. 그 maincommdb.c (별도 파일, util_admin.cutil_service.c 와 분리)에 산다는 점이다:

  • cubrid commdb -P — 등록된 서버 프로세스 list.
  • cubrid commdb -Ocub_master 가 아는 모든 것 (broker 와 pl 포함) list.
  • cubrid commdb -S <db> — 한 서버의 shutdown 요청.
  • cubrid commdb -A — 등록된 모든 서버의 shutdown 요청.
  • cubrid commdb -h <host> — 원격 cub_master target (드물다 — 대부분 동작은 로컬).

논리적으로는 cubrid admin 패밀리의 일부이지만 자기 파일에 사는 이유는 master-process RPC 를 직접 말하기 때문이다 — subprocess 를 fork 하지 않는다는 뜻이다. 운영자가 이름으로 직접 부르는 일은 드물다 — util_service.c 의 서비스-패밀리 process_* 함수들이 master 에 query / signal 을 보내기 위해 사용한다.

cubrid --helpcubrid --versionus_Service_map 의 특수 항목 (UTIL_HELPUTIL_VERSION sentinel)으로 감지된다. help 는 패밀리 list + 짧은 usage 를 렌더링하고, version 은 print_admin_version 으로 forward 한다. print_admin_versionlibcubridsa 에서 version 심볼을 dlsym 해서 호출한다는 뜻이다. 바이너리가 정보 호출을 위해 순전히 라이브러리를 dlopen 하는 두 곳 중 하나다 (다른 하나는 print_admin_usage).

심볼역할
maincubrid 바이너리의 진입점; 패밀리 / admin / help / version 감지, 비트마스크 cross-check 실행, 패밀리 또는 admin arm 으로 디스패치
parse_argoption-map 배열을 문자열 해석; option type 또는 ER_GENERIC_ERROR 반환
us_Service_map50 항목의 패밀리 + admin-verb 토큰 표
us_Command_map17 항목의 동작 명령 표와 패밀리 수용 비트마스크
us_Property_mapload_properties 가 읽는 cubrid.conf 속성 키의 정렬된 리스트
load_properties / finalize_properties / get_propertycubrid.conf 리더와 accessor
process_service / process_server / process_broker / process_manager / process_heartbeat / process_pl / process_gateway패밀리별 디스패처; 각각 (command_type, argc, argv, ...) 를 받음
process_adminadmin arm — util_admin.c::main 동등 로직을 cubrid <verb> 호출 케이스를 wrap
proc_execute_internal / proc_execute / proc_execute_hide_cmd_args옵션 출력 redirect 와 arg masking 을 가진 fork/execv wrapper
process_mastercub_master 에 요청 발사 (shutdown all 등)
is_server_running / is_broker_running / is_gateway_running / is_manager_running / is_pl_runningPID-파일 또는 process-list probe; start 가 이미 실행 중 감지에, status 가 렌더링에 사용
util_log_write_command / util_log_write_result / util_log_write_errid / util_log_write_errstrcubrid_utility.log writer
print_message / print_result / command_stringHelp 와 end-of-run 메시지

Verb 표와 SA/CS 디스패치 (util_admin.c)

섹션 제목: “Verb 표와 SA/CS 디스패치 (util_admin.c)”
심볼역할
mainStandalone admin 바이너리 진입점 (레거시 호환 shim 의 execvp target 으로 사용); util_service.cprocess_admin 과 같은 디스패치
ua_Utility_Mapmode/argv-min/option-table 을 가진 42 항목의 admin verb 표
util_get_utility_indexverb 이름 → 표 인덱스
util_get_library_nameverb 의 mode → 라이브러리 이름 (libcubridsa 또는 libcubridcs)
util_get_function_nameverb 이름 → 로드된 라이브러리의 진입 심볼
util_parse_argument표의 GETOPT_LONGUTIL_ARG_MAP 컬럼을 사용하는 verb 별 argv 파서
print_admin_usage / print_admin_versionusage/version 텍스트를 위해 dlsymlibcubridsa 심볼로 forward

Verb 별 옵션 / arg-map 타입 (util_common.c, util_admin.c)

섹션 제목: “Verb 별 옵션 / arg-map 타입 (util_common.c, util_admin.c)”

각 verb 가 짝지은 GETOPT_LONG ua_<Verb>_Option[] (getopt 짧은 →long 표)와 UTIL_ARG_MAP ua_<Verb>_Option_Map[] (argv 파싱 결과 schema: 위치 인자 + 플래그별 값 type)를 선언한다. 둘 다 util_admin.c 의 정적 배열이다. 모양:

// util_admin.c 안 verb 마다 반복되는 패턴
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}}, // 위치 인자
{BACKUP_DESTINATION_PATH_S, {ARG_STRING}, {0}},
{BACKUP_REMOVE_ARCHIVE_S, {ARG_BOOLEAN}, {0}},
/* ... */
{0, {0}, {0}}
};

표 중심 접근은 새 admin verb 추가가 순수 선언적 변경 임을 의미한다 — 두 배열을 선언하고, ua_Utility_Map 에 행을 추가 하고, verb 함수를 libcubridsa 또는 libcubridcs (또는 SA_CS 에는 둘 다)에 작성한다는 뜻이다.

심볼역할
main프로그램 이름 감지; 매칭 arg-번역 표 선택; argv 재작성; cubrid (또는 csql / commdb)으로 execvp
ua_Util_table프로그램 이름 → 모던 verb 이름 + verb 별 arg map (19 행)
ua_Create_map / ua_Rename_map / ua_Copy_map / …verb 별 짧은→long arg 번역 (18 표, 레거시 바이너리당 하나)
get_long_arg_by_old_arg단일 arg lookup
convert_argvargv walking, lookup 으로 항목 교체, sentinel -1 표시 args drop, 알 수 없는 -x 에 에러
심볼역할
main-P/-O/-S/-A/-h 파싱; cub_master 연결 열기; MASTER_CONN_LIST_* / MASTER_REQ_SHUTDOWN_* 요청 중 하나 전송; 응답 렌더 또는 적용
process_master_* (in util_service.c)패밀리 디스패처가 commdb 를 fork 하지 않고 master 요청을 발사할 수 있게 해주는 더 높은 수준 wrapper
심볼경로
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
패밀리 MASK_* 상수와 UTIL_TYPE_* 문자열src/executables/util_service.c:143
process_admin 호출 사이트src/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 (선언)src/executables/util_admin.c:1015
util_get_library_name / util_get_function_name (선언)src/executables/util_admin.c:1013–1014
util_front.c::mainsrc/executables/util_front.c:369
ua_Util_table (레거시 프로그램 이름 → 모던 verb)src/executables/util_front.c:278
convert_argvsrc/executables/util_front.c:333

심볼 이름이 정규 anchor 이고, 라인 번호는 updated: 날짜에 스코프된 힌트이다.

  • main, 한 바이너리. util_service.cutil_admin.c 둘 다 main 을 정의한다. CMake 빌드는 util_service.c::maincubrid 바이너리의 진입으로 링크 하고, util_admin.c::main 은 레거시 util_front.c shim 의 execvp target 으로 쓰이는 별도 cub_admin 바이너리에 빌드 된다는 뜻이다. 둘이 ua_Utility_Map 을 공유 (util_admin.c 에서 선언, util_service.c::process_admin 에서 참조)해서 사용 자가 어떤 바이너리를 쳤든 verb 해석이 일관된다.
  • SA_CS 해석 시점. SA_CS 로 표시된 verb 들은 라이브러리 선택을 util_parse_argument 후로 미룬다 — 그 파싱이 사용자가 -S (SA mode)를 줬는지 -C (CS mode)를 줬는지를 발견하기 때문이다. 즉 파싱 에러 경로도 성공 경로와 같은 라이브러리 로드 로직을 돈다 — args 가 거절되었을 때도 usage 메시지가 올바른 라이브러리에서 나오게 보장한다.
  • is_valid_arg = false 가 verb 함수로 forward 된다. arg 가 잘못 파싱되었어도 verb 가 로드되고 호출된다 — verb 가 자기 usage 를 렌더할 수 있게 하기 위함이다. valid_arg 필드가 verb 에게 일을 시도하지 말고 usage 를 출력하고 exit 하라고 말한다는 뜻이다.
  • load_properties 는 reload 너머로 idempotent 하지 않다. startup 에 cubrid.conf 를 한 번 읽는다. cubrid service start 다음에 수동 cubrid.conf 편집과 cubrid service status 가 편집을 보지 못한다 — 운영자는 cubrid 호출을 재시작해야 한다. heartbeat-only reload 동작이 예외이다 — ha-config 를 독립적으로 재읽기.
  • MASK_PLstart / stop 에서 제외된다. PL 은 per-database 서버 (cub_servercub_pl 을 fork)에 의해 간접적으로 시작/ 중지된다 — 운영자는 restart 또는 status 로만 상호작용한다. cubrid pl start <db> 를 시도하면 비트마스크 검사에서 실패 한다.
  • 레거시 shim 은 로깅하지 않는다. util_front.c::mainutil_log_write_* 를 하지 않는다 — audit 로그는 execvp 후 모던 cubrid 바이너리가 채운다는 뜻이다. 즉 레거시 바이너리를 쓰는 스크립트가 audit 로그에 모던 verb 호출로만 나타나고, 진입점이 레거시 이름이었다는 기록은 없다.
  • commdb 이중 경로. commdb 는 standalone 바이너리와 cubrid commdb 둘 다로 존재한다. 둘 다 cub_master 에 직접 말한다. util_service.cprocess_master 도우미들은 commdb 바이너리의 in-process 우회로 — 패밀리 디스패처가 fork 할 필요 가 없게 해준다는 점이다.
  • Verb 별 도움말 정도. cubrid <verb> --help 가 로드된 라이브러리의 usage 함수에 위임된다 (verb 마다 다른 형식). 공통 메시지 카탈로그에서 가져오는 통합 cubrid --help <verb> 형식 은 문서화되지 않았고 현재 메시지 파일들에 존재하지 않을 수 있다.
  • cubrid_utility.log 회전. 로그는 append-only 다 — 문서화 된 회전 정책이 없다. 빈번한 서비스 동작이 있는 long-running cluster 는 로그를 무한정 누적한다.
  • 비트마스크 충돌 audit. (패밀리, 동작) cross-check 표는 수동 유지된다 — 빠진 마스크 비트가 유효한 조합을 조용히 거절한다는 뜻이다. 모든 (패밀리, 동작) 쌍을 walk 해서 마스크 집합이 문서화된 동작과 맞는지 확인하는 테스트는 없다.
  • 레거시 프로그램 이름 list 의 stale. ua_Util_table 은 19 행으로 고정되어 있다. ua_Utility_Map 에 추가된 새 admin verb (memmon, flashback, tde)는 레거시 짧은-이름 항목이 없다 — 운영자는 cubrid <verb> 형식을 써야 한다는 뜻이다. 의도된 것인지 shim 의 점진적 은퇴인지는 문서화되지 않았다.
  • cubrid commdb 은퇴. process_master 가 in-process 로 쓸 수 있는 상황에서, standalone commdb 바이너리는 대체로 중복이다. ship 을 유지하는 것은 backward-compat 입장이다 — 은퇴 일정은 기록되지 않았다.
  • src/executables/util_service.ccubrid 바이너리의 main, 패밀리 디스패처, 비트마스크 cross-check, 속성 로더, 패밀리별 process_* 함수
  • src/executables/util_admin.c — admin-verb 표 (ua_Utility_Map), verb 별 옵션 표, 라이브러리-이름과 함수- 이름 도우미, standalone cub_admin main
  • src/executables/util_common.c — 공유 verb 도우미, 메시지 출력, UTIL_FUNCTION_ARG 정의
  • src/executables/util_cs.c — CS-mode verb 구현 (library_name == libcubridcs 일 때 dlsym 되는 심볼들)
  • src/executables/util_sa.c — SA-mode verb 구현
  • src/executables/util_front.c — 레거시 호환 shim; verb 별 arg- 번역 표; 모던 진입으로 execvp
  • src/executables/util_support.cutility_load_library, utility_load_symbol, utility_load_print_error, 메시지 카탈로그 wrapper
  • src/executables/commdb.ccub_master probe 바이너리
  • src/executables/AGENTS.md — agent 가이드; binary→source map
  • 인접 문서: cubrid-csql.md (다른 주요 사용자 대면 바이너리; csql_launcher.c 로 같은 SA/CS launcher 패턴을 사용), cubrid-loaddb.md / cubrid-compactdb.md / cubrid-backup-restore.md (이미 전용 문서가 있는 verb), cubrid-master-process.md (cub_master 자체), cubrid-heartbeat.md (heartbeat 패밀리 lifecycle), cubrid-broker.md (broker 패밀리 lifecycle), cubrid-pl-javasp.md (pl 패밀리 lifecycle), cubrid-system-parameters.md (load_properties 가 소비하는 cubrid.conf 속성)