Сперва я собирался назвать статью "CLI своими руками", но потом подумал, что тему стоит расширить и рассказать зачем это вообще всё нужно, про динамическое конфигурирование. Еще упомяну, что долгое время занимался маршрутизаторами, в частности системой управления, являюсь автором open-source проекта https://klish.libcode.org, непосредственно с этим управлением связанного, а сейчас участвую в разработке веб-сервера Angie (форк Nginx, который основали разработчики оригинального Nginx). Поэтому многие примеры будут из области маршрутизаторов и веб-сервера, из того, что я видел изнутри.
Представим, у нас есть система, которой нужно управлять. Пусть это будет некая встроенная система, вроде маршрутизатора или управляемого коммутатора, где требуется управлять всем устройством целиком, а может просто одиночный сервис, который крутится в операционной системе общего назначения. И нам надо, чтобы целевая система могла конфигурироваться "на-лету" - динамически, чтобы промежуток времени, когда функции системы недоступны, был минимальным, а в идеале его не было бы вообще.
Что касается одиночного сервиса, то задача чаще всего решается следующим образом. Есть текстовый конфигурационный файл. Если сервис уже запущен и нам надо изменить настройки, то редактируем конфигурационный файл и посылаем процессу сигнал. Обычно (но не обязательно) это SIGHUP. Получив сигнал, сервис заново перезачитывает свой конфигурационный файл и узнаёт новые настройки. Достаточно ли это динамично? Во многих случаях - да. Для большинства одиночных сервисов этого более, чем достаточно. Для простых случаев все то, о чем будет рассказано далее - стрельба из пушки по воробьям.
А когда текстового конфига и SIGHUP-а недосточно?
Во-первых, если сервис большой, конфигурационный файл большой, а в настройках хочется изменить одну маленькую циферку. По сигналу сервис будет перезачитывать и парсить весь конфигурационный файл целиком. Да и применяться он будет зачастую целиком, потому что вычленить изменения - это, в общем случае, нетривиальная задача. А когда заново применяется сложный конфиг, то это пересоздание внутренних структур, потеря текущего состояния, может быть статистики. Проблему пытаются решать. Например в веб-сервере Angie некоторые динамические параметры способны пережить перезагрузку конфига. Для этого, параллельно с работающим мастер-процессом (процесс, который читает конфиг, порождает рабочие процессы, и ждет сигналов от пользователя) запускается новый мастер-процесс с новым конфигом, новые рабочие процессы и уже они, через область разделяемой памяти, пытаются "спасти" информацию из старых процессов. Затем старые процессы постепенно завершаются по мере закрытия соединений, которые через них были установлены. Мягко говоря, не простой путь. Я этого не застал, но возможно именно после этого в проекте появилось REST API. Но об этом позже.
Второй случай, когда текстового конфига и сигнала недостаточно, это если хочется управлять системой удаленно по сети. Можно, конечно, зайти по ssh, поправить конфиг и послать сигнал. Да, часто такое решение годится, но не всегда. Неудобно, плохо поддается автоматизации.
И, наконец, третий случай, если управлять надо не одиночным сервисом, а двумя и более объектами. Маршрутизатор - прекрасный пример такого случая, экстремальный зоопарк объектов управления. Там в маршрутизаторе и ядро (например Linux) и куча разрозненных сервисов. Способы конфигурирования каждой подсистемы совершенно разные. Например, управление сетевыми интерфейсами или настройка фильтров - обратитесь к ядру. Нет никакого текстового файла конфигурации, а надо вызывать утилиты или непосредственно говорить с ядром через системные вызовы. Куча сервисов, каждый из которых считает, что его способ конфигурирования и формат конфигурационного файла - самый правильный и удобный. Эффективно управлять... вернее вообще управлять такой системой - малореально. Как минимум надо помнить где лежат конфиги подсистем, их формат, особенности и т.д. Что делать? Надо унифицировать конфигурирование, приводить способ управления всеми подсистемами к единому виду. Условно говоря, иметь один конфиг. Да еще не забывать о динамическом конфигурировании.