Skip to content
isdnetworks
Go back

코드에 적힌 값이 실제 값이 아니다

장비가 예상과 다른 주소로 통신하고 있었다. 코드를 봤다.

#define DEFAULT_HOST "192.0.2.10"

이 주소가 아니었다. 실제로는 다른 곳으로 나가고 있었다.

Table of contents

Open Table of contents

값의 출처가 넷이었다

추적해 보니 값을 정하는 자리가 넷이었다.

const char *host = DEFAULT_HOST;              /* 1. 소스 기본값 */
read_config("/etc/dev.conf", &cfg);           /* 2. 설정 파일이 덮음 */
if (getenv("DEV_HOST")) host = getenv(...);   /* 3. 환경변수가 덮음 */
parse_args(argc, argv);                        /* 4. 실행 인자가 덮음 */

뒤에 오는 것이 앞을 덮는데 실제로 쓰인 것은 argv 로 들어온 넷째였고 기동 스크립트가 넘긴 값이었다.

소스를 아무리 읽어도 안 나오는데 값이 systemd 유닛 쪽 스크립트에 있기 때문이다.

이 구조 자체는 흔하고 편리하다. DEFAULT_HOST 를 보고 동작을 단정하면 반드시 틀린다.

전체 흐름 — 확인 순서

값 하나를 확인하려면 네 곳을 봐야 하고 순서가 정해져 있으니 뒤에서부터 본다.

1) 기동 스크립트 / systemd unit  ← 실행 인자
2) 환경변수                       ← 프로세스 환경
3) 설정 파일                      ← /etc/…
4) 소스 기본값                    ← 여기까지 왔으면 앞의 셋이 다 없는 것

뒤에서부터 보면 대개 첫 곳에서 끝나는데 앞에서부터 보면 소스에서 값을 찾은 순간 멈추게 된다.

찾았다고 생각한 값이 실제 값이 아니다. 순서를 거꾸로 잡는 것만으로 헛수고가 없어졌다.

검증 — 실행 중인 프로세스에서 읽기

더 확실한 방법은 프로세스가 실제로 무엇을 들고 있는지를 /proc 에서 보는 것이었다.

tr '\0' '\n' < /proc/<pid>/cmdline   # 실행 인자
tr '\0' '\n' < /proc/<pid>/environ   # 환경변수

/proc 에서 읽으면 설정을 재구성해 추론하는 것이 아니라 지금 도는 프로세스에서 가져오는 것이 된다.

이것을 먼저 하는 쪽이 나았다. 설정 파일을 고쳤는데 반영이 안 되는 경우도 여기서 갈린다.

파일은 바뀌었는데 environ 이 옛 값을 들고 있으면 재기동을 안 한 것이다. 파일을 보는 것과 프로세스를 보는 것이 다른 확인이었다.

상태를 보여주는 명령을 넣었다

매번 /proc/<pid>/cmdline 을 뒤지는 것도 번거로워 프로그램에 상태 출력을 넣었다.

$ devctl status
host      203.0.113.5    (from: argv)
port      9000           (from: config file)
interval  30             (from: default)
timeout   5              (from: env)

devctl status 가 값과 함께 어디서 왔는지를 찍는데 이것이 생각보다 쓸모 있었다.

설정 파일을 고쳤는데 왜 안 바뀌느냐는 물음에 (from: argv) 를 보여 주면 끝난다. 환경변수나 인자가 덮고 있다는 것을 그 줄이 말해 준다.

구현은 간단해서 값을 읽을 때 src 에 출처를 같이 기록해 두면 된다.

struct setting { const char *val; const char *src; };

val 옆에 src 를 두는 것뿐인데 조사 시간이 크게 줄었다.

참고 자료 — 문서에 적은 우선순위

운영 문서에 이 순서를 명시했다.

설정 우선순위 (뒤가 앞을 덮음)
  소스 기본값 < 설정 파일 < 환경변수 < 실행 인자

현재 적용된 값은 devctl status 로 확인한다.

이것을 안 적어 두면 각자 자기가 아는 곳만 고친다. 어떤 사람은 설정 파일을 고치고 어떤 사람은 스크립트를 고친 뒤 둘 다 고쳤는데 안 된다고 말한다.

같은 말을 하는데 가리키는 자리가 달라서 대화가 안 맞는다. 순서를 문서 한 줄로 고정하니 그 대화가 없어졌다.

반대 함정도 있었다

한 가지를 더 겪었는데 /etc/dev.conf 에 없는 항목을 고치려는 경우다.

파일을 열면 열 줄쯤 있는데 프로그램이 읽는 항목은 서른 개다. 나머지 스무 개는 기본값으로 돈다.

파일에 없다고 그 설정이 없는 것이 아니고 파일은 기본값과 다르게 할 것만 담는다.

그래서 전체 항목 목록을 기본값과 함께 따로 뒀다.

# 전체 설정 항목 (값은 기본값)
host      = 192.0.2.10
port      = 8080
interval  = 60
timeout   = 10
retry     = 3
...

이 목록이 없으면 무엇을 바꿀 수 있는지 알 방법이 없다. 소스를 읽어야 나오는데 그러면 설정이라는 것의 뜻이 없어진다.

정리


Share this post on:

Previous Post
답은 데이터시트 각주에 있었다
Next Post
붙이면 사라지는 버그