Мне повезло, у меня была конкертная задача. На реализацию я потратил ~4 дней. Что слишком долго для задачи, но.. Это моя первая программа.
Задача: написать генератор конфигурационных файлов для связки бекенд(nginx)-фронтенд(apache22) которые работают на одном сервере.
У нас уже была реализация этой системы. Работает это так. В БД mysql через вебинтерфейс заносится информация о домене. Хранится
- document_root
- domain
- aliases — через перенос строки
- server — номер сервера
- updated
- updated_nginx
updated и updated_nginx устанавляиваются в 1 когда происходит обновление или добавление домена.
Конфигурация виртуальных хостов типичная и является шаблоном.
В общем задача
- Принять и обработать аргументы командной строки. При этом число обязательных аргументов должно быть минимально.
- Прочитать конфигурацию соеденения с mysql сервером. Она хранится в файле. Там-же хранится номер сервера.
- Получить из БД mysql все домены которые нужно обновить и сложить в структуру.
- Пройтись по этим доменам циклом. Сгенерить для каждого конфигурацию по шаблону. Обновить поля updated дабы показать что мы сделали конфигурацию.
- Пройтись по директории содержащий конфиги и сгенерить конфигурационный файл содержащий include со всеми файлами в директории.
- Перезагрузить вебсервер, дабы тот подхватил новую конфигурацию.
Строки и стандартные функции
Во-первых я не сразу понял как принято работать со строками в Си. Для конкатенации строк я использовал функции из библиотеки libstrfunc. Выглядит это уродливо
sbuf_add(selectQueryWhere, "server=");
sbuf_add(selectQueryWhere, server->buf);
if (isApache) {
if (isUpdateAll==0)
sbuf_add(selectQueryWhere, " and updated=1");
Вместо таких замудрений можно было использовать подстановку через
sprintf();
Либо копирование в строковой буфер с проверкой длины через функции стандартной библиотеки strcpy, strcat..
char b_aliases[BSIZE];
// Clean \r\n from alias
strcpy(b_aliases, replace(domain.aliases, "\r\n", " "));
Действительно для чего полезна библиотека libstrfunc это функция replace.
О длине кол-ве элементов в массиве
Скажу сразу, просто так узнать нельзя. Нормальный подход в Си это передать через аргументы функции указатели для массива и для кол-ва элементов.
Тут есть такой момент.. Здесь про **указатель указателей с примером кода.
Об обработки параметров через getopt_long
Муторное это занятие доложу я вам. Т.е. само то сделать приём аргументов не вопрос. А вот сделать что-бы оно работало по принципу
Если есть соответствующий аргумент, использвать его, в противном случае значение по-умолчанию.
Кроме того я сделал так что обязательным для программы является один параметр режим. Он может быть —nginx или —apache. Об остальном программа может догадаться сама.
static char* usageStr =
"Usage: %s [OPTION]... [--nginx | --apache]\n"
" -h, --help This screen\n"
" -v, --verbose Show debug messages\n"
"\n"
" -n, --nginx Use nginx configuration\n"
" -a, --apache Use apache configuration\n"
//" -d, --pid Override path to PID file\n"
" -r, --cmd_restart Command for restarting webserver\n"
"\n"
" -t, --vhost_templ Override path to vhost template\n"
" -p, --vhosts_path
Override path to vhosts configuration files\n"
" -i, --vhosts_index Override path to vhosts configuration index file\n"
"\n"
" -c, --config Override domenka config file\n"
" -l, --update_all Ignore updated status\n"
" -s, --server Override domenka serverId domenka\n"
"\n";
const char* DEFAULT_VHOSTS_TEMPL_APACHE = "/usr/local/etc/domenka/apache.templ";
const char* DEFAULT_VHOSTS_TEMPL_NGINX = "/usr/local/etc/domenka/nginx.templ";
//
const char* DEFAULT_VHOST_INDEX_APACHE = "/usr/local/etc/apache22/hosting.conf";
const char* DEFAULT_VHOST_INDEX_NGINX = "/usr/local/etc/nginx/hosting.conf";
//
const char* DEFAULT_VHOSTS_PATH_APACHE = "/usr/local/etc/apache22/hosting";
const char* DEFAULT_VHOSTS_PATH_NGINX = "/usr/local/etc/nginx/hosting";
//
/*
const char* DEFAULT_PID_APACHE = "/var/run/httpd.pid";
const char* DEFAULT_PID_NGINX = "/var/run/nginx.pid";
*/
//
const char* DEFAULT_CMD_RESTART_APACHE = "/usr/local/etc/rc.d/apache22 restart";
const char* DEFAULT_CMD_RESTART_NGINX = "/usr/local/etc/rc.d/nginx restart";
//
const char* DEFAULT_CONFIG = "/usr/local/etc/domenka/hosting.conf";
О перезагрузке сервера
Сервер перегружать через передачу сигнала HUP по id получается только один раз. Я хотел получать PID через правильные файлы, /var/run/httpd.pid, /var/run/nginx.pid, но после HUP, как и следовало ожидать, pid содержащийся в этих файлах не соответвут реальному.
Есть вариант перегружать по-имени процесса. Но для nginx это не катит потому как нужно гасить только мастер процесс.
Я сделал с использованием стартовых скриптов etc/rc.d/
Об ошибках и отладке
Программа умеет делать —verbose сообщая откуда она планирует брать шаблоны, конфиг, складывать конфигурацию виртуальных хостов, генерировать индексный файл и какой сервер использовать для получения информации по доменам.
Кроме того на все вещи вроде файл или директория не найдены, выдаётся вменяемая ошибка.
А на отсутвие обязательного параметра режим, выдаётся хелп ключей и соответвующее поясняющее сообщение.
Чего не хватает
Инсталлятора не хватает. Нужно освоить Make, autoconf. Так-же пакеты и порты во FreeBSD. :)