(KO) CUBRID 유틸리티 모음 — commdb, gencat, generate_timezone, daemon, cubrid_version, pl bootstrap helper
왜 옴니버스인가
섹션 제목: “왜 옴니버스인가”여기서 다루는 6 개 utility 는 모두 자기 main 함수를 가지지만,
각각 30–50 KB 짜리 분석을 받기에는 너무 작다. 한 문서로 묶는 근거는
세 가지다. 모두 CUBRID 라이브러리 함수나 작은 POSIX helper 위에
얹은 얇은 wrapper 라는 점, 어느 것도 큰 유틸리티들이 쓰는
SA/CS dual-launcher 패턴에 들어가지 않는다는 점, 각각이 좁은 운영
관심사 하나로 매핑된다는 점이다. 이 문서 한 편이 여섯 개를 모두
다룰 수 있는 이유가 여기에 있다.
6 개는 세 패밀리로 나뉜다:
| 패밀리 | 도구 |
|---|---|
| 런타임 probe / helper | commdb, pl, daemon |
| 빌드타임 generator | gencat, generate_timezone |
| Trivia | cubrid_version |
런타임 probe 는 운영자가 직접 부르거나 다른 유틸리티가 fork 해서
쓰는 도구다. 빌드타임 generator 는 CUBRID 빌드 동안 돌면서
엔진에 컴파일되거나 함께 배포되는 artefact 를 만든다. trivia
하나는 PRODUCT_STRING 위에 얹은 no-op shell 이다.
이 도구들이 통합 admin CLI 의 cubrid <verb> 형식으로도 존재하는
경우 (cubrid commdb, cubrid pl ping, cubrid --version),
cubrid 바이너리의 디스패처 (cubrid-cub-admin.md 참조)는 이
standalone 바이너리를 fork 하는 대신 in-process 동등물로
라우팅한다. 그래도 standalone 바이너리는 여전히 설치되고 옛
invocation 모양 그대로 호출할 수 있다.
commdb — master-process RPC client
섹션 제목: “commdb — master-process RPC client”소스 파일은 src/executables/commdb.c (약 1480 줄).
용도는 통합 cubrid 바이너리를 거치지 않고 cub_master 의
listening 소켓으로 직접 probe 하고 명령을 보내는 것이다.
cubrid commdb 형식보다 먼저 등장했고 두 형식 모두 지금도 함께
배포된다.
Argv 모양:
commdb [-P] [-O] [-S <db>] [-A] [-h <host>] [-c <db>] [--server-shutdown=...] [--all-list] [--server-list] ...짧은 플래그 (-P, -O, -S, -A, -h)가 레거시 형식이다.
사용자가 모던 cubrid commdb 를 부르면 util_front.c 의
us_Commdb_map 이 short 플래그를 long 형식으로 번역한 뒤
여기로 execvp 한다.
전송하는 메시지는 다음과 같다:
| 플래그 | Master 오피코드 | 결과 |
|---|---|---|
-P (server-list) | GET_SERVER_LIST | 등록된 cub_server 당 한 줄 출력 |
-O (all-list) | GET_ALL_LIST | 서버 + broker + pl 출력 |
-S <db> (한 개 shutdown) | KILL_SLAVE_SERVER | 명명된 서버에 shutdown 요청 |
-A (모두 shutdown) | KILL_ALL_HA_PROCESS 다음 per-server KILL_SLAVE_SERVER | master 가 아는 모든 것 shutdown |
-c <db> (서버 status) | GET_SERVER_STATE | 한 서버의 HA 모드 + ping 출력 |
오피코드 분류 체계와 per-opcode 핸들러 집합은
cubrid-master-process.md §“요청 디스패치” 에 정리되어 있다.
여전히 따로 존재하는 이유는 두 가지다. 첫째, 모던 cubrid commdb
는 util_service.c 안에서 master 와 in-process 로 통신하는
process_master helper 로 구현되며, 정상 admin 스크립트에서는
이 경로가 적합하다. 반면 standalone commdb 는 cubrid.conf
로딩이나 패밀리-디스패처 로직을 전혀 요구하지 않는 순수 RPC
client 라서, cubrid 자체가 config 를 읽지 못하는 비상 상황의
probe 도구로 적합하다. 둘째, cub_master RPC 인터페이스는
통합 admin CLI 보다 몇 년 먼저 자리 잡았기 때문에, 많은 운영자
스크립트가 지금도 commdb 를 직접 호출한다.
위치 힌트:
| 심볼 | 경로 |
|---|---|
main | src/executables/commdb.c:1209 |
pl — cub_pl JVM bootstrap helper
섹션 제목: “pl — cub_pl JVM bootstrap helper”소스 파일은 src/executables/pl.cpp (약 720 줄).
용도는 데이터베이스마다 하나씩 띄우는 cub_pl JVM 프로세스
(JavaSP 와 PL/CSQL 호스팅; cubrid-pl-javasp.md §“Process
topology” 참조)에 대한 lifecycle 명령이다.
cubrid pl ping/start/stop/restart/status <db> 가 이 바이너리를
호출하지만, 직접 부르는 것도 가능하다.
Argv 모양:
pl <command> <db_name><command> 는 ping, start, stop, restart, status 중 하나.
본체 로직:
// pl.cpp::main (의역)os_set_signal_handler (SIGABRT/TERM/ILL/FPE/BUS/SEGV/SYS, pl_signal_handler);er_init (NULL_DEVICE, ER_NEVER_EXIT); // ping 용 메시지 억제
pl_check_argument (argc, argv, command, db_name);pl_check_database (db_name, pathname); // db 존재 확인
if (command != "ping") er_init ("pl/<db>_pl.err", ER_NEVER_EXIT); // 진짜 에러 로그
PL_SERVER_INFO pl_info = ...;pl_get_server_info (db_name, pl_info); // $CUBRID/var/pl_<db>.info 읽기
if (command == "ping") { if (pl_info.pid == PL_PID_DISABLED || is_terminated_process (pl_info.pid)) { printf ("NO_PROCESS"); exit (NO_ERROR); } /* cub_pl 소켓에 연결 시도하고 ping 패킷 교환 */}/* ... start / stop / restart / status 분기 ... */$CUBRID/var/ 아래의 데이터베이스별 pl_<db>.info 파일은
cub_pl 이 시작될 때 자기 PID 와 포트를 적어두는 rendezvous
파일이다 (cubrid-pl-javasp.md §“Discovery and rendezvous”
참조). 이 helper 는 그 파일을 읽어 직접 동작 (ping, status,
stop) 하거나, cub_pl 을 새로 fork (start) 하거나 재실행
(restart) 한다.
별도 바이너리인 이유는 cub_pl lifecycle 이 데이터베이스 서버
(cub_server) 가 동작 중이지 않을 때도 작동해야 하기 때문이다.
JVM 은 독립된 프로세스이고 별도로 점검·관리할 수 있다. 이 동작을
in-process 호출로 util_service.c::process_pl 에 묶으면
cub_master 의 매개를 강제하게 되는데, standalone helper 가 바로
그 의존성을 회피한다.
위치 힌트:
| 심볼 | 경로 |
|---|---|
main | src/executables/pl.cpp:160 |
daemon — 작은 fork/setsid wrapper
섹션 제목: “daemon — 작은 fork/setsid wrapper”소스 파일은 src/executables/daemon.c (약 40 줄).
용도는 controlling terminal 에서 detach 해야 하지만 daemonise 관용구를 자기 코드에 직접 박아 넣기 싫은 다른 바이너리들의 helper 역할이다. 특정 스크립트나 일회성 바이너리 안에서 호출되며 운영자가 직접 부르는 일은 거의 없다.
동작은 표준 double-fork 다. fork() 한 뒤 자식이 setsid() 를
부르고, 다시 fork() 해서 grand-child 가 controlling terminal 을
잡지 못하게 만든 다음, parent 와 중간 자식은 종료한다. systemd /
launchd 를 쓸 수 없을 때 Debian 의 start-stop-daemon --background
가 하는 일과 같다.
구현이 충분히 짧아서 다른 바이너리들은 이 관용구를 자기 코드에
복제해 쓰는 경우가 많다 (master.c::css_daemon_start 가 정규 예;
cubrid-master-process.md 참조). 이 standalone helper 는 외부
도구로 호출하는 방식을 선호하는 일부 빌드타임 스크립트와 테스트
harness 를 위해 따로 남아 있다.
cubrid_version — version-string printer
섹션 제목: “cubrid_version — version-string printer”소스 파일은 src/executables/cubrid_version.c (약 37 줄).
용도는 PRODUCT_STRING (CUBRID 11.3.1.0001 같은 정규 CUBRID
버전 banner) 를 출력하고 종료하는 것이다.
Argv 모양은 없다. 플래그도 없고, 인자가 들어와도 무시한다.
본체는 다음과 같다:
int main (int argc, char *argv[]) { printf ("%s\n", PRODUCT_STRING); return EXIT_SUCCESS;}(실제 파일은 조금 더 정교하다. release-info 라이브러리를 끌어와
rel_release_string() 으로 빌드타임 release patch 에 따라 출력이
달라지게 만든다.)
별도 바이너리인 이유는 버전 문자열만 필요할 뿐 cubrid --version
의 더 긴 출력을 파싱하기는 싫은 shell 스크립트를 위한 편의다.
운영자와 CI 파이프라인은 grep/awk 에 잘 맞는 fixed-format
출력을 얻기 위해 cubrid_version 을 호출한다.
gencat — POSIX 메시지-카탈로그 컴파일러
섹션 제목: “gencat — POSIX 메시지-카탈로그 컴파일러”소스 파일은 src/executables/gencat.c (약 1130 줄).
유래는 NetBSD gencat(1) 이다 (NetBSD 의
src/usr.bin/gencat/gencat.c). 저작권 표기를 보존한 채 거의 그대로
import 했다. CUBRID 빌드가 자기 사본을 직접 가지고 다니는 이유는
두 가지다. 모든 플랫폼의 libc 가 gencat 바이너리를 제공하지는
않는다는 점, 그리고 CUBRID 가 쓰는 on-disk 형식 (이 도구가 만드는
binary .cat 파일에서 읽어가는 MSGCAT_CATALOG_* 표) 이 NetBSD
해석과 바이트 단위로 호환되어야 한다는 점이다.
컴파일 입력은 locale/component 당 하나씩 쓰는 .msg 소스 파일이고,
출력은 binary .cat 파일이다.
$set 11 "First message"2 "Second message with %s placeholders"$set 21 "Another set"이 입력은 set/메시지 표를 담은 binary .cat 으로 변환되어 런타임
catopen()/catgets() 가 읽는다. CUBRID 는 사용자 대면 텍스트
전반을 이 메커니즘으로 처리한다. 코드베이스에서 흔히 보이는
MSGCAT_CATALOG_CSQL / MSGCAT_CATALOG_UTILS /
MSGCAT_CATALOG_LOCALE 가 그 예다.
이 도구는 빌드타임 전용이다. 운영자가 직접 돌릴 일이 없고, CMake
빌드가 locale 마다 gencat 을 호출해 $CUBRID/locales/<locale>/
아래에 설치되는 .cat 파일을 생산한다.
위치 힌트:
| 심볼 | 경로 |
|---|---|
main | src/executables/gencat.c:205 |
generate_timezone — tzdata to C source 컴파일러
섹션 제목: “generate_timezone — tzdata to C source 컴파일러”소스 파일은 src/executables/generate_timezone.cpp (62 줄).
용도는 IANA tzdata 를 입력으로 받아 cubrid_timezones 공유
라이브러리에 컴파일되는 C 소스 파일 (timezones.c) 을 만들어내는
빌드타임 도구다. 런타임 tz 함수 (tz_id_to_str, datetime offset
산술 등) 가 생성된 표를 읽어 쓴다. 새 IANA release 로 업데이트할
때는 새 tzdata 를 가지고 이 도구를 다시 돌린 다음 다시 빌드하면
된다.
Argv 모양:
generate_timezone <input_tzdata_path> <output_timezones.c_path>본체는 다음과 같다:
int main (int argc, char **argv) { if (argc != 3) { usage (); return EXIT_FAILURE; }
const char *tzdata_input_path = argv[1]; const char *timezones_dot_c_output_path = argv[2]; char checksum_str[TZ_CHECKSUM_SIZE + 1] = {0};
if (timezone_compile_data (tzdata_input_path, TZ_GEN_TYPE_NEW, NULL, timezones_dot_c_output_path, checksum_str) != NO_ERROR) { return EXIT_FAILURE; } return EXIT_SUCCESS;}timezone_compile_data (tz_compile.h 선언, src/base/ 구현) 를
감싸는 62 줄짜리 wrapper 일 뿐이다. IANA tzdata 파일의 실제 파싱은
그 함수 쪽에 있고, 이 바이너리는 그저 CLI 진입점이다.
빌드타임 전용 도구다. 운영자는 직접 돌리지 않으며 CMake 의
cubrid_timezones 타깃이 빌드 중에 호출한다.
이 도구와 cubrid gen_tz, cubrid dump_tz (ua_Utility_Map 에
등록된 런타임 변형) 가 한 묶음을 이루어, 빌드타임 집합에 들어
있지 않은 locale 이 필요한 운영자에게 런타임에 데이터베이스별
tz 표를 만들 수 있는 길을 제공한다 (cubrid-timezone.md 참조).
위치 힌트:
| 심볼 | 경로 |
|---|---|
main | src/executables/generate_timezone.cpp:38 |
Cross-check 노트
섹션 제목: “Cross-check 노트”- 6 개 모두 통합
cubridadmin CLI 보다 먼저 등장했다. 각각은 레거시 스크립트가 계속 동작하도록 standalone 바이너리 형태로 배포된다.cubrid안의 모던 동등물 (cubrid commdb,cubrid pl ping,cubrid --version등) 은 같은 라이브러리 함수나 같은 RPC 를 호출하지만, 이 바이너리를 fork/exec 하지 않고util_service.c의 디스패처를 거쳐 처리한다. gencat과generate_timezone은 빌드타임 전용이다. 이 둘을 런타임 utility 라고 부르면 분류 자체가 어긋난다. CMake 빌드가 컴파일 산출물로 취급하기 때문에src/executables/에 나타날 뿐, 운영자 도구로는 배포되지 않는다.commdb -h <host>로 cross-host probe 가 가능하지만 실제로는 거의 쓰이지 않는다.cub_master의 기본 포트가 production 에서는 firewall 로 막혀 있고, 대부분의 운영 워크플로가 로컬에 한정되기 때문이다.pl은 개념적으로util_service.c::process_pl의 자식 격이다. 운영자가cubrid pl <verb>를 호출하면 먼저process_pl에 도달하고, 거기서 다시pl을 fork 하거나 (verb 일부에 한해) 같은 라이브러리 함수를 in-process 로 부른다. standalone 바이너리가 정규 구현이고,process_pl은 통합-CLI 쪽 어댑터다.daemon.c는 의도적으로 최소한이다. 사용자 대면 도구가 아니라, C 를 직접 쓰지 않으면서 forking helper 가 필요한 스크립트를 위한 빌드 artefact 다.
열린 질문
섹션 제목: “열린 질문”commdb대cubrid commdbdeprecation 문제.process_master가 in-process 로 동작하는 지금, standalone 바이너리는 사실상 중복이다. 그러나 deprecation 일정이 따로 명문화되어 있지 않다. 두 형식이 모두 배포되는 이유는 standalone 을 제거하면 레거시 운영 스크립트가 깨지기 때문이다.gencat의 upstream sync. import 이후 NetBSD 소스가 계속 변해왔다. 주기적으로 다시 sync 하면 버그 픽스를 받아올 수 있지만 이미 배포된.cat파일과의 바이트 호환성이 깨질 위험이 있다. 현재로서는 그 trade-off 를 적극적으로 평가하지 않고, import 된 버전을 freeze 된 것으로 취급한다.generate_timezone의 테스트 가능성. 62 줄짜리 wrapper 에는 unit test 가 없고, 유일한 검증은 CMake 빌드 성공이다. 잘못된 tzdata 는timezone_compile_data에서 유의미한 에러를 내며EXIT_FAILURE로 전파되지만, wrapper 자체가 CI 에서 별도 단위로 행사되지는 않는다.cubrid_version과cubrid --version의 차이. 두 형식이 출력 포맷이 약간 다르다. standalone 은 정확히PRODUCT_STRING만 찍고,cubrid --version은 메시지 카탈로그에서 가져온 장식 몇 줄을 덧붙인다. 출력을 파싱하는 스크립트는 한쪽을 명시적으로 골라 쓰고, 함부로 다른 쪽으로 전환하지 말아야 한다.
Sources
섹션 제목: “Sources”src/executables/commdb.c— master-process RPC clientsrc/executables/pl.cpp—cub_plJVM lifecycle helpersrc/executables/daemon.c— fork/setsid wrappersrc/executables/gencat.c— NetBSD-import 된 POSIX 메시지- 카탈로그 컴파일러src/executables/generate_timezone.cpp— tzdata → C 소스 빌드 타임 도구src/executables/cubrid_version.c— version printersrc/executables/AGENTS.md— agent 가이드- 인접 문서:
cubrid-cub-admin.md(이 도구들의 모던 invocation 대부분을 흡수한 통합 admin CLI),cubrid-master-process.md(commdb가 호출 대상으로 삼는 데몬; 이 client 가 보내는 요청 오피코드 분류 체계를 정리),cubrid-pl-javasp.md(pl이 관리하는 JVM;pl이 읽는 rendezvous 파일을 정리),cubrid-timezone.md(generate_timezone이 빌드타임에 생산하는 결과를 소비하는 런타임 tz 인프라)