(KO) CUBRID cubrid Admin CLI — Verb 디스패처, SA/CS 라우팅 라이브러리 로딩, Service · Server · Broker · Heartbeat 패밀리
이론적 배경
섹션 제목: “이론적 배경”통합 admin CLI 는 데이터베이스 엔진이 DBA 가 스크립팅해야 하는 비-인터랙티브 작업을 위해 ship 하는 단일 바이너리이다 — 서버 시작/중지, 백업, 통계 dump, 트랜잭션 list, runaway 쿼리 kill, 카탈로그 재구축. 디자인 질문은 많은 작은 동작을 하나의 진입점 뒤에 어떻게 조직해서 디스패치 로직 자체가 새 verb 를 추가하는 데 병목이 되지 않게 하느냐이다.
두 가지 디자인 선택이 공간을 지배한다는 점이다:
-
평탄 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). -
빌트인 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
은 파싱 시점에 실패한다 — reset 이 MASK_BROKER | MASK_GATEWAY
이고 MASK_HEARTBEAT 가 아니기 때문이다.
일반 DBMS 디자인
섹션 제목: “일반 DBMS 디자인”| 엔진 | Admin CLI 모양 | Verb 디스패치 | Subprocess 관리 |
|---|---|---|---|
| PostgreSQL | 많은 별도 바이너리 (pg_ctl, pg_basebackup, pg_dump, pg_restore, pg_isready, pgbench) | per-binary main; pg_ctl 이 cubrid <action> server 의 가장 가까운 유사물 | pg_ctl start 가 postgres 를 fork; lifecycle 은 data/postmaster.pid 의 PID 파일 |
| MySQL | 다중 바이너리 (mysqladmin, mysqldump, mysqlimport, mysqlcheck, mysql_upgrade); mysqld 가 서버 자체 | per-binary main; mysqladmin 이 catch-all command tool | systemd / mysql.server 스크립트; per-instance PID 파일 |
| Oracle | srvctl, crsctl, lsnrctl, expdp, impdp, rman; cluster 동작에 대한 메타 도구는 srvctl | srvctl <verb> <noun> [args] — verb-noun 중첩 디스패치 | OHASD / CSSD / CRSD 데몬; cluster 레지스트리는 OCR |
| MongoDB | mongod 는 서버, mongosh 는 shell, mongodump/mongorestore/mongoimport/mongoexport 는 동작별 바이너리 | per-binary main | mongod --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_server 와
cub_pl 인스턴스에 대한 long-lived supervisor 이고
(cubrid-master-process.md 와 cubrid-heartbeat.md 참조),
cubrid 바이너리는 대부분 작은 RPC 로 cub_master 에게 일을
부탁하는 thin client 이다. cub_master 가 필요 없는 동작
(createdb, unloaddb 같은 데이터베이스-admin verb)은 SA-mode
엔진 라이브러리를 직접 로드하는 별도 코드 경로로 디스패치된다는
뜻이다.
CUBRID 의 접근
섹션 제목: “CUBRID 의 접근”한 바이너리, 두 디스패치 arm
섹션 제목: “한 바이너리, 두 디스패치 arm”cubrid 바이너리의 main 은 util_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 이 돌고, 둘 다 아니면 에러이다.
서비스 패밀리 arm
섹션 제목: “서비스 패밀리 arm”패밀리 + 명령이 2D grid 를 이룬다. (패밀리, 명령) 의 cell 이
(family_mask & command_mask) != 0 일 때 유효 이다 (마스크는
패밀리에 대해서는 us_Service_map, 명령에 대해서는
us_Command_map 에서). 마스크는:
// util_service.c — 패밀리 비트마스크MASK_SERVICE = 1 << 0MASK_SERVER = 1 << 1MASK_BROKER = 1 << 2MASK_MANAGER = 1 << 3MASK_HEARTBEAT = 1 << 4MASK_ADMIN = 1 << 5MASK_PL = 1 << 6MASK_GATEWAY = 1 << 7MASK_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.c 의 process_* 패밀리 정적 함수들이 그 다음에
실제 작업을 한다는 뜻이다:
| 패밀리 | 디스패처 | 무엇을 하는가 |
|---|---|---|
service | process_service | 메타 orchestrator — cubrid.conf 의 service 절에 나열된 모든 컴포넌트 (SERVICE_START_SERVER, SERVICE_START_BROKER 등)를 시작/중지; init 스크립트가 사용 |
server | process_server | 데이터베이스 이름당 cub_server 를 fork; is_server_running() + PID-파일 lookup 사용; ha_mode 가 켜져 있으면 서버를 heartbeat 에 등록 가능 |
broker | process_broker | cub_broker 와 broker 별 CAS 풀 관리; acl reload 가 broker ACL 파일 재읽기; info 가 broker 별 통계 dump |
manager | process_manager | cubrid_manager HTTP 데몬 (web admin 도구) 관리 |
heartbeat | process_heartbeat (sub-dispatcher 들 _start/_stop/_deregister/_status/_reload/_util/_replication 와 함께) | HA orchestration — cubrid-heartbeat.md 참조 |
pl | process_pl (sub-dispatcher 들 _restart/_status 와 함께) | 데이터베이스 이름당 cub_pl 관리 — cubrid-pl-javasp.md §Process topology 참조 |
gateway | process_gateway | gateway 기능을 위한 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_ONLYverb 에는libcubridsa—createdb,restoredb,optimizedb,installdb,diagdb,patchdb,alterdbhost,genlocale,dumplocale,synccolldb,gen_tz,dump_tz,restoreslave. 이 verb 들은 호출 동안 엔진이 된다 (SA 라이브러리는 엔진 전체를 링크해두고 있다).CS_ONLYverb 에는libcubridcs—lockdb,killtran,plandump,statdump,tranlist,changemode,copylogdb,applylogdb,applyinfo,acldb,checksumdb,vacuumdb,flashback,memmon. 이 verb 들은 동작 중인cub_server와 네트워크로 대화한다.SA_CSverb 에는libcubridsa또는libcubridcs—backupdb,addvoldb,spacedb,cleanfiledb,checkdb,loaddb,unloaddb,compactdb,paramdump,tde. 사용자가 verb 자기 옵션 표에서-S/--SA-mode또는-C/--CS-mode로 고른다. 라이브러리 선택은dlopen전에 파싱 시점에 해석 된다.
verb 의 진입 함수 이름은 verb 이름의 고정 변환에서 온다 (예:
loaddb → loaddb_user per the table; 대부분의 verb 는 동일
매핑). util_get_function_name 도우미가 이 매핑을 한다.
데이터베이스-admin verb 의 전체 집합:
| 패밀리 | Verb |
|---|---|
| 데이터베이스 lifecycle | createdb, deletedb, renamedb, copydb, installdb, restoredb, restoreslave, alterdbhost |
| 볼륨 / 공간 | addvoldb, spacedb, cleanfiledb, diagdb, checkdb, patchdb |
| 벌크 import / export | loaddb, 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 흐름 상세
섹션 제목: “util_service.c::main 흐름 상세”// 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 / ... 로 디스패치 */진입 흐름에서의 세 가지 관찰:
- Argv 순서 관용. 디스패처가 먼저
argv[1]을 시도하고 실패하면argv[2]를 다시 시도한다. 패밀리 토큰의 위치가 고정되지 않았던 옛 init 스크립트의start cubrid <db>호출 을 수용하기 위함이다. UTIL_PID_ENVVAR_NAME가 설정되어, 자식 프로세스 (forkedcub_server,cub_broker등)가 audit 로그에서 자기를 발신cubrid호출과 상관시킬 수 있게 한다.load_properties()가cubrid.conf를 한 번 읽어us_Property_map(서비스 시작용 데이터베이스 list, broker list, manager list, heartbeat config, gateway list)을 채우고, 이들이 이어지는 모든 process-family 동작에 먹여진다.
로깅: util_log_write_*
섹션 제목: “로깅: util_log_write_*”모든 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 가 잘못
동작했거나 데이터베이스가 사라졌을 때 무엇을 했는지 재구성하는
데 유용하다.
레거시 호환 shim (util_front.c)
섹션 제목: “레거시 호환 shim (util_front.c)”이전 CUBRID 릴리스는 동작당 바이너리를 ship 했다 — createdb,
loaddb, unloaddb, compactdb, backupdb, restoredb,
addvoldb, spacedb, lockdb, killtran 등 거기에 csql 과
commdb 도. 모던 CUBRID 도 여전히 이 이름들을 ship 하지만 각각
이 util_front.c 에서 빌드된 front-end shim 이다 — 그
main 은:
basename(argv[0])— 자기 프로그램 이름 — 을 본다.ua_Util_table[](프로그램 이름 → 모던 verb 이름 → arg-번역 표를 짝지은 19 줄 표)에서 매칭 항목을 찾는다.[cubrid, "<modern-verb>", ...번역된 args...]형태의 새argv를 구축한다.- 각 옛 짧은 형식 arg (
-p,-c,-l,-mv, …)를 verb 별 arg map (ua_Create_map,ua_Backup_map등)을 사용해서 모던 long 형식 (--pages,--comment,--log-path,--more-volumes-file, …)로 번역한다. - 번역된 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로 로깅한다.)
commdb: master-process probe
섹션 제목: “commdb: master-process probe”cubrid commdb (그리고 레거시 bare commdb 바이너리)는
서비스-list / 서버-shutdown 명령을 위해 cub_master 에 대한 thin
client 이다. 그 main 은 commdb.c (별도 파일, util_admin.c
와 util_service.c 와 분리)에 산다는 점이다:
cubrid commdb -P— 등록된 서버 프로세스 list.cubrid commdb -O—cub_master가 아는 모든 것 (broker 와 pl 포함) list.cubrid commdb -S <db>— 한 서버의 shutdown 요청.cubrid commdb -A— 등록된 모든 서버의 shutdown 요청.cubrid commdb -h <host>— 원격cub_mastertarget (드물다 — 대부분 동작은 로컬).
논리적으로는 cubrid admin 패밀리의 일부이지만 자기 파일에
사는 이유는 master-process RPC 를 직접 말하기 때문이다 —
subprocess 를 fork 하지 않는다는 뜻이다. 운영자가 이름으로
직접 부르는 일은 드물다 — util_service.c 의 서비스-패밀리
process_* 함수들이 master 에 query / signal 을 보내기 위해
사용한다.
Help 와 version
섹션 제목: “Help 와 version”cubrid --help 와 cubrid --version 은 us_Service_map 의 특수
항목 (UTIL_HELP 와 UTIL_VERSION sentinel)으로 감지된다.
help 는 패밀리 list + 짧은 usage 를 렌더링하고, version 은
print_admin_version 으로 forward 한다. print_admin_version 은
libcubridsa 에서 version 심볼을 dlsym 해서 호출한다는 뜻이다.
바이너리가 정보 호출을 위해 순전히 라이브러리를 dlopen 하는
두 곳 중 하나다 (다른 하나는 print_admin_usage).
소스 워크스루
섹션 제목: “소스 워크스루”진입과 디스패치 (util_service.c)
섹션 제목: “진입과 디스패치 (util_service.c)”| 심볼 | 역할 |
|---|---|
main | cubrid 바이너리의 진입점; 패밀리 / admin / help / version 감지, 비트마스크 cross-check 실행, 패밀리 또는 admin arm 으로 디스패치 |
parse_arg | option-map 배열을 문자열 해석; option type 또는 ER_GENERIC_ERROR 반환 |
us_Service_map | 50 항목의 패밀리 + admin-verb 토큰 표 |
us_Command_map | 17 항목의 동작 명령 표와 패밀리 수용 비트마스크 |
us_Property_map | load_properties 가 읽는 cubrid.conf 속성 키의 정렬된 리스트 |
load_properties / finalize_properties / get_property | cubrid.conf 리더와 accessor |
process_service / process_server / process_broker / process_manager / process_heartbeat / process_pl / process_gateway | 패밀리별 디스패처; 각각 (command_type, argc, argv, ...) 를 받음 |
process_admin | admin 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_master | cub_master 에 요청 발사 (shutdown all 등) |
is_server_running / is_broker_running / is_gateway_running / is_manager_running / is_pl_running | PID-파일 또는 process-list probe; start 가 이미 실행 중 감지에, status 가 렌더링에 사용 |
util_log_write_command / util_log_write_result / util_log_write_errid / util_log_write_errstr | cubrid_utility.log writer |
print_message / print_result / command_string | Help 와 end-of-run 메시지 |
Verb 표와 SA/CS 디스패치 (util_admin.c)
섹션 제목: “Verb 표와 SA/CS 디스패치 (util_admin.c)”| 심볼 | 역할 |
|---|---|
main | Standalone admin 바이너리 진입점 (레거시 호환 shim 의 execvp target 으로 사용); util_service.c 의 process_admin 과 같은 디스패치 |
ua_Utility_Map | mode/argv-min/option-table 을 가진 42 항목의 admin verb 표 |
util_get_utility_index | verb 이름 → 표 인덱스 |
util_get_library_name | verb 의 mode → 라이브러리 이름 (libcubridsa 또는 libcubridcs) |
util_get_function_name | verb 이름 → 로드된 라이브러리의 진입 심볼 |
util_parse_argument | 표의 GETOPT_LONG 과 UTIL_ARG_MAP 컬럼을 사용하는 verb 별 argv 파서 |
print_admin_usage / print_admin_version | usage/version 텍스트를 위해 dlsym 한 libcubridsa 심볼로 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
에는 둘 다)에 작성한다는 뜻이다.
레거시 호환 shim (util_front.c)
섹션 제목: “레거시 호환 shim (util_front.c)”| 심볼 | 역할 |
|---|---|
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_argv | argv walking, lookup 으로 항목 교체, sentinel -1 표시 args drop, 알 수 없는 -x 에 에러 |
Master-process probe (commdb.c)
섹션 제목: “Master-process probe (commdb.c)”| 심볼 | 역할 |
|---|---|
main | -P/-O/-S/-A/-h 파싱; cub_master 연결 열기; MASTER_CONN_LIST_* / MASTER_REQ_SHUTDOWN_* 요청 중 하나 전송; 응답 렌더 또는 적용 |
process_master_* (in util_service.c) | 패밀리 디스패처가 commdb 를 fork 하지 않고 master 요청을 발사할 수 있게 해주는 더 높은 수준 wrapper |
위치 힌트 (2026-05-05 기준)
섹션 제목: “위치 힌트 (2026-05-05 기준)”| 심볼 | 경로 |
|---|---|
util_service.c::main | src/executables/util_service.c:534 |
us_Service_map | src/executables/util_service.c:152 |
us_Command_map | src/executables/util_service.c:227 |
us_Property_map | src/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::main | src/executables/util_admin.c:1093 |
ua_Utility_Map | src/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::main | src/executables/util_front.c:369 |
ua_Util_table (레거시 프로그램 이름 → 모던 verb) | src/executables/util_front.c:278 |
convert_argv | src/executables/util_front.c:333 |
심볼 이름이 정규 anchor 이고, 라인 번호는 updated: 날짜에
스코프된 힌트이다.
Cross-check 노트
섹션 제목: “Cross-check 노트”- 두
main, 한 바이너리.util_service.c와util_admin.c둘 다main을 정의한다. CMake 빌드는util_service.c::main을cubrid바이너리의 진입으로 링크 하고,util_admin.c::main은 레거시util_front.cshim 의execvptarget 으로 쓰이는 별도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-onlyreload동작이 예외이다 — ha-config 를 독립적으로 재읽기.MASK_PL가start/stop에서 제외된다. PL 은 per-database 서버 (cub_server가cub_pl을 fork)에 의해 간접적으로 시작/ 중지된다 — 운영자는restart또는status로만 상호작용한다.cubrid pl start <db>를 시도하면 비트마스크 검사에서 실패 한다.- 레거시 shim 은 로깅하지 않는다.
util_front.c::main은util_log_write_*를 하지 않는다 — audit 로그는execvp후 모던cubrid바이너리가 채운다는 뜻이다. 즉 레거시 바이너리를 쓰는 스크립트가 audit 로그에 모던 verb 호출로만 나타나고, 진입점이 레거시 이름이었다는 기록은 없다. commdb이중 경로.commdb는 standalone 바이너리와cubrid commdb둘 다로 존재한다. 둘 다cub_master에 직접 말한다.util_service.c의process_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 로 쓸 수 있는 상황에서, standalonecommdb바이너리는 대체로 중복이다. ship 을 유지하는 것은 backward-compat 입장이다 — 은퇴 일정은 기록되지 않았다.
Sources
섹션 제목: “Sources”src/executables/util_service.c—cubrid바이너리의main, 패밀리 디스패처, 비트마스크 cross-check, 속성 로더, 패밀리별process_*함수src/executables/util_admin.c— admin-verb 표 (ua_Utility_Map), verb 별 옵션 표, 라이브러리-이름과 함수- 이름 도우미, standalonecub_adminmainsrc/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- 번역 표; 모던 진입으로execvpsrc/executables/util_support.c—utility_load_library,utility_load_symbol,utility_load_print_error, 메시지 카탈로그 wrappersrc/executables/commdb.c—cub_masterprobe 바이너리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속성)