Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Первае шаги в программировании Linux

.doc
Скачиваний:
13
Добавлен:
08.04.2015
Размер:
377.34 Кб
Скачать

Для примера создадим программу, которая в качестве параметра получает название функции, которую она будет использовать в работе. Например, это будут математические функции возведения в степень. Создадим сначала динамическую библиотеку. Пишем ее код:

double power2(double x){

return x*x;

};

double power3(double x){

return x*x*x;

};

double power4(double x){

return power2(x)*power2(x);

};

//......

Сохраняем его в файл lib.c и создаем динамическую библиотеку libpowers.so следующими командами:

dron:~# gcc -fPIC -c lib.c

dron:~# gcc -shared lib.o -o libpowers.so

Теперь создаем основную программу в файле main.c:

#include <stdio.h>

/* заголовочный файл для работы с динамическими библиотеками */

#include <dlfcn.h>

int main(int argc, char* argv[]){

void *ext_library; // хандлер внешней библиотеки

double value=0; // значение для теста

double (*powerfunc)(double x); // переменная для хранения адреса функции

//загрузка библиотеки

ext_library = dlopen("/root/libpowers.so",RTLD_LAZY);

if (!ext_library){

//если ошибка, то вывести ее на экран

fprintf(stderr,"dlopen() error: %s\n", dlerror());

return 1;

};

//загружаем из библиотеки требуемую процедуру

powerfunc = dlsym(ext_library, argv[1]);

value=3.0;

//выводим результат работы процедуры

printf("%s(%f) = %f\n",argv[1],value,(*powerfunc)(value));

//закрываем библиотеку

dlclose(ext_library);

};

Код главной программы готов. Требуется его откомпилировать с использованием библиотеки dl:

dron:~# gcc main.c -o main -ldl

Получим программный файл main, который можно тестировать. Наша программа должна возводить значение 3.0 в требуемую нами степень, которая задается названием функции. Давайте попробуем:

dron:~# ./main power2

power2(3.000000) = 9.000000

dron:~# ./main power3

power3(3.000000) = 27.000000

dron:~# ./main power4

power4(3.000000) = 81.000000

dron:~#

Ну, как ?! Круто !!! Мы используем какие-то функции, зная лишь их название. Представьте открывающиеся возможности для программ, на основе этого метода можно создавать плагины для программ, модернизировать какие-то части, добавлять новые возможности и многое другое.

Шаг 9 - Инициализация динамических библиотек

До сих пор мы писали с Вами простые динамические библиотеки, поэтому у нас не болела голова об инициализации внутренних переменных. А представьте себе более сложную ситуацию, когда функции библиотеки для работы требуют правильно инициализированные переменные. Ну, например, для работы функции нужен буфер или массив.

Специально для таких случаев в библиотеках можно задавать инициализирующую и деинициализирующую функции:

  • void _init() - инициализация

  • void _fini() - деинициализация

Чтобы понять, что к чему, введем в нашей библиотеке lib.c переменную test и возвращающую ее функцию:

char *test;

char *ret_test(){

return test;

};

Пишем основную программу main.c. Она очень похожа на предыдущий наш проект, поэтому можете его модифицировать:

#include <stdio.h>

#include <dlfcn.h>

int main(){

void *ext_library;

double value=0;

char * (*ret_test)();

ext_library = dlopen("libtest.so",RTLD_LAZY);

if (!ext_library){

fprintf(stderr,"dlopen() error: %s\n", dlerror());

return 1;

};

ret_test = dlsym(ext_library,"ret_test");

printf("Return of ret_test: \"%s\" [%p]\n",(*ret_test)(),(*ret_test)());

dlclose(ext_library);

};

После компиляции всего этого хозяйства мы получим результат:

dron:~# gcc -c lib.c -fPIC

dron:~# gcc -shared lib.o -o libtest.so

dron:~# gcc -o main main.c -ldl

dron:~# ./main

Return of ret_test: "(null)" [(nil)]

dron:~#

Как видите переменная test оказалась равной NULL, а нам бы хотелось нечто другое. Ну, так давайте посмотрим как работают функции _init() и _fini(). Создадим вторую библиотеку lib1.c:

#include <stdlib.h>

char *test;

char *ret_test(){

return test;

};

void _init(){

test=(char *)malloc(6);

if (test!=NULL){

*(test+0)='d';

*(test+1)='r';

*(test+2)='o';

*(test+3)='n';

*(test+4)='!';

*(test+5)=0;

};

printf("_init() executed...\n");

};

void _fini(){

if (test!=NULL) free(test);

printf("_fini() executed...\n");

};

Теперь пробуем компилировать:

dron:~# gcc -c lib1.c -fPIC

dron:~# gcc -shared lib1.o -o libtest.so

lib1.o: In function `_init':

lib1.o(.text+0x24): multiple definition of `_init'

/usr/lib/crti.o(.init+0x0): first defined here

lib1.o: In function `_fini':

lib1.o(.text+0xc0): multiple definition of `_fini'

/usr/lib/crti.o(.fini+0x0): first defined here

collect2: ld returned 1 exit status

dron:~#

Опаньки... Облом. Что же это такое ?! Оказывается кто-то уже использовал эти функции до нас и программа не может слинковаться. После долгого копания в нескольких чужих исходниках я получил ответ на этот вопрос. Оказывается, чтобы избавиться от мешающей библиотеки надо использовать ключ компилятора -nostdlib. Попробуем:

dron:~# gcc -shared -nostdlib lib1.o -o libtest.so

dron:~#

Смотрите-ка, все прекрасно скомилировалось. Теперь попробуем запустить main:

dron:~# ./main

_init() executed...

Return of ret_test: "dron!" [0x8049c20]

_fini() executed...

dron:~#

Ну как ? Помоему классно. Теперь можно спокойно создавать для работы библиотеки правильно инициализированные переменные. Однако классные эти штуки - динамические библиотеки ! А Вы что хотели ? Тот же Windows только на своих DLL и живет. А Linux ничем не хуже...

Шаг 10 - Передача опций в программу - getopt

Почти всем сложным программам для работы требуется входные параметры (опции, аргументы - называйте как хотите), от значения которых строится последовательность работы алгоритма заложенного в программе или используются различные источники данных.

Вы наверняка знаете, что передача параметров в программу на C/C++ осуществляется через массив функции main(). Так повелось, что он называется argv (от arguments values - значения аргументов), но в принципе его можно назвать и по другому. Количество этих параметров передается через переменную argc (от arguments counter - счетчик аргументов).

Программа, для работы которой требуется набор входных параметров задается при помощи специального определения функции main():

int main(int argc, char *argv[]{

};

int main(int argc, char **argv){

};

Давайте напишем маленькую программку, которая выводит значения переданных параметров:

// программа test.c

#include <stdio.h>

int main(int argc, char *argv[]){

int i=0;

for (i=0;i<argc;i++){

printf("Argument %d: %s\n",i,argv[i]);

};

};

Сохраняем в файл test.c и компилируем:

dron:~# gcc test.c -o test

После этого попробуем запустить программу:

dron:~# ./test

Argument 0: ./test

Передадим несколько параметров:

dron:~# ./test qwe sdf fgh hjk kl 123 --help

Argument 0: ./test

Argument 1: qwe

Argument 2: sdf

Argument 3: fgh

Argument 4: hjk

Argument 5: kl

Argument 6: 123

Argument 7: --help

В качестве первого параметра программе всегда передается ее имя и таким образом программа может узнать свое название, т.е. имя файла, в котором она содержится.

Но моя цель не говорить о том, как передаются параметры, а как с ними работать. Для начала надо вспомнить, что в системе Linux существует два вида параметров: короткие и длинные. Короткие параметры начинаются с одного дефиса и имеют длину в один символ, их просто и быстро набирать в командной строке. Длинные параметры начинаются с двух дефисов и могут иметь длинное имя, которое целесообразно использовать в скриптах (чтобы потом можно было вспомнить, что и как происходит). Кроме этого любой параметр может иметь значение, а может и не иметь. Приведу для примера несколько параметров:

-h - короткий параметр

--help - длинный параметр

-s 10 - параметры со значениями

--size 10

--size=10

Так вот, существует несколько специальных функций предназначенных для разбора списка переданных параметров:

  • int getopt(...) - Обрабатывает короткие параметры

  • int getopt_long(...) - Обрабатывает короткие и длинные параметры

  • int getopt_long_only(...) - Обрабатывает параметры только как длинные

Давайте разберемся с работой первой функции - getopt(...). Ее определение выглядит следующим образом:

#include <unistd.h>

int getopt(int argc, char * const argv[],

const char *optstring);

extern char *optarg;

extern int optind, opterr, optopt;

Эта функция последовательно перебирает переданные параметры в программу. Для работы в функцию передается количество параметров argc, массив параметров argv[] и специальная строка optstring, в которой перечисляются названия коротких параметров и признаки того, что параметры должны иметь значение. Например, если программа должна воспринимать три параметра a, b, F , то такая строка бы выглядела как "abF". Если параметр должен иметь значение, то после буквы параметра ставится двоеточие, например параметр F и d имеют значения, а параметры e, a и b не имеют, тогда эта строка могла бы выглядеть как "eF:ad:b". Если параметр может иметь (т.е. может и не иметь) значение, то тогда ставится два знака двоеточия, например "a::" (это специальное расширение GNU). Если optstring содержит "W:", то тогда параметр -W opt переданный в программу, будет восприниматься как длинный параметр --opt. Это связано с тем, что параметр W зарезервирован в POSIX.2 для расширения возможностей.

Для перебора параметров функцию getopt() надо вызывать в цикле. В качестве результата возвращется буква названия параметра, если же параметры кончились, то функция возвращает -1. Индекс текущего параметра хранится в optind, а значение параметра помещается в optarg (указатель просто указывает на элемент массива argv[]). Если функция находит параметр не перечисленный в списке, то выводится сообщение об ошибке в stderr и код ошибки сохраняется в opterr, при этом в качестве значения возврящается "?". Вывод ошибки можно запретить, если установить opterr в 0.

#include <stdio.h>

#include <unistd.h>

int main(int argc, char *argv[]){

int rez=0;

// opterr=0;

while ( (rez = getopt(argc,argv,"ab:C::d")) != -1){

switch (rez){

case 'a': printf("found argument \"a\".\n"); break;

case 'b': printf("found argument \"b = %s\".\n",optarg); break;

case 'C': printf("found argument \"C = %s\".\n",optarg); break;

case 'd': printf("found argument \"d\"\n"); break;

case '?': printf("Error found !\n");break;

};

};

};

Попробуем скомпилировать данную программку и запустить:

dron:~# gcc test.c -o test

dron:~# ./test -a -b -d -C

found argument "a".

found argument "b = -d".

found argument "C = (null)".

dron:~# ./test -a -b -C -d

found argument "a".

found argument "b = -C".

found argument "d"

dron:~# ./test -a -b1 -C -d

found argument "a".

found argument "b = 1".

found argument "C = (null)".

found argument "d"

dron:~# ./test -b1 -b2 -b 15

found argument "b = 1".

found argument "b = 2".

found argument "b = 15".

Давайте посмотрим, как функция getopt вылавливает ошибки. Попробуем задать параметр, которого нет в списке:

dron:~# ./test -h -a

./test: invalid option -- h

Error found !

found argument "a".

Как я и говорил, функция вывела сообщение об ошибке в stderr. Давайте выключим вывод сообщений, для этого надо где-то в программе перед вызовом функции вставить opterr=0;. Компилируем и запускаем:

dron:~# ./test -h -a

Error found !

found argument "a".

Теперь, как видите, сообщение больше не выдается, зато как и раньше можно обработать ошибку самому.

Шаг 11 - Передача длинных опций в программу - getopt_long

Парсинг длинных параметров командной строки достаточно сложный процесс, поэтому бибилиотека GNU C Library имеет специальную функцию getopt_long(), которая может работать одновременно и с длинными и с короткими параметрами. Для работы только с длинными именами параметров существует функция getopt_long_only.

Для того, чтобы работать с этими функциями Вам потребуется подключить файл getopt.h. Выглядят эти функции следующим образом:

#define _GNU_SOURCE

#include <getopt.h>

int getopt_long(int argc, char * const argv[],

const char *optstring,

const struct option *longopts, int *longindex);

int getopt_long_only(int argc, char * const argv[],

const char *optstring,

const struct option *longopts, int *longindex);

Для работы функции getopt_long ей нужны следующие данные:

  • argc - счетчик количества параметров командой строки argc.

  • argv - значения парамеров командной строки argv[].

  • shortopts - список коротких названий параметров, которые мы изучали в "Шаг 10 - Передача опций в программу - getopt".

  • longopts - специальный массив с названиями длинных параметров.

  • longindex - указатель на переменную, в которую будет помещен индекс текущего параметра из массива longopts.

Основным отличием этих фукнций от getopt является потребность в специальном массиве. О нем и поговорим. Массив longopts состоит из записей struct option имеющих следующий вид:

struct option {

const char *name;

int has_arg;

int *flag;

int val;

};

В первом поле name задается название длинного параметра.

Поле has_arg определяет нужно ли для этого параметра значение. Для этого в getopt.h определены специальные значения:

#define no_argument 0

#define required_argument 1

#define optional_argument 2

Как видите, если значение has_arg равно 0 (no_argument), то параметр не должен иметь значение, если 1 (required_argument), то параметр должен иметь значение. Если же значение для параметра опционально, то has_arg равен 3 (optional_argument).

Поле flag задает указатель на флаг, в который помещается значение val, если найден данный параметр (сама функция при этом возвращает 0). Если указатель равен NULL, то функция возвращает значение val в качестве результата работы.

Поле var содержит значение, которое помещается в flag или возвращается в качестве результата работы функции.

Последняя запись массива longopts должна содержать нулевые значения, для того чтобы функция могла однозначно определить конец массива.

Использовать данную функцию можно несколькими способами. Первый способ самый простой, и заключается в установке флажков программы с помощью данной функции в зависимости от входных параметров.

Давайте посмотрим пример longopt1.c:

#include <stdlib.h>

#include <stdio.h>

#include <getopt.h>

int main (int argc, char *argv[]){

int flag_a = 0;

int flag_b = 0;

int flag_c = 0;

const char* short_options = "abc";

const struct option long_options[] = {

{"opta",no_argument,&flag_a,1},

{"optb",no_argument,&flag_b,10},

{"optc",no_argument,&flag_c,-121},

{NULL,0,NULL,0}

};

while (getopt_long(argc,argv,short_options,

long_options,NULL)!=-1);

printf("flag_a = %d\n",flag_a);

printf("flag_b = %d\n",flag_b);

printf("flag_c = %d\n",flag_c);

printf("\n");

};

После компиляции gcc longopt1.c -o longopt1 получим программу. Вот некоторые результаты работы:

dron~# ./longopt1

flag_a = 0

flag_b = 0

flag_c = 0

dron~# ./longopt1 --opta

flag_a = 1

flag_b = 0

flag_c = 0

dron~# ./longopt1 --optb --optc

flag_a = 0

flag_b = 10

flag_c = -121

dron~# ./longopt1 -a -b -c

flag_a = 0

flag_b = 0

flag_c = 0

Как видите, когда функция увидела параметры --opta, --optb или --optc она сразу же установила переменные flag_a, flag_b и flag_с значениями, которые были указаны в массиве long_options. Но посмотрите на короткие параметры -a, -b и -c. Они не были задействованы. А все от того, что в качестве результата работы функция возвращает:

  • если короткий параметр, то его название (т.е. символ)

  • если длинный параметр, то значение val при flag=NULL, иначе 0 и флагу flag присваивается значение val.

Мы с вами обработку коротких параметров не предусмотрели, если Вы сейчас модифицируете код, то сможете увидеть и их:

// добавьте переменную

int rez;

// новый цикл обработки параметров

while ((rez=getopt_long(argc,argv,short_options,

long_options,NULL))!=-1)

{

printf("rez: %d = \'%c\'\n",rez,rez);

};

Если сейчас запустить программу, то она выдаст следующее:

dron~# ./longopt1 -abc

rez: 97 = 'a'

rez: 98 = 'b'

rez: 99 = 'c'

flag_a = 0

flag_b = 0

flag_c = 0

dron~# ./longopt1 -a -c -g

rez: 97 = 'a'

rez: 99 = 'c'

./a.out: invalid option -- g

rez: 63 = '?'

flag_a = 0

flag_b = 0

flag_c = 0

Теперь можно обрабатывать и короткие параметры, а чтобы это делать все сразу, существует как раз второй метод использования этой функции. Это когда указатели flag устанавливают в NULL, а значения val устанавливают в названия коротких параметров. При этом вся обработка результатов происходит в switch структуре. Давайте попробуем создать файл longopt2.c:

#include <stdlib.h>

#include <stdio.h>

#include <getopt.h>

int main (int argc, char *argv[]){

const char* short_options = "hs::f:";

const struct option long_options[] = {

{"help",no_argument,NULL,'h'},

{"size",optional_argument,NULL,'s'},

{"file",required_argument,NULL,'f'},

{NULL,0,NULL,0}

};

int rez;

int option_index;

while ((rez=getopt_long(argc,argv,short_options,

long_options,&option_index))!=-1){

switch(rez){

case 'h': {

printf("This is demo help. Try -h or --help.\n");

printf("option_index = %d (\"%s\",%d,%c)\n",

option_index,

long_options[option_index].name,

long_options[option_index].has_arg,

long_options[option_index].val

);

break;

};

case 's': {

if (optarg!=NULL)

printf("found size with value %s\n",optarg);

else

printf("found size without value\n");

break;

};

case 'f': {

printf("file = %s\n",optarg);

break;

};

case '?': default: {

printf("found unknown option\n");

break;

};

};

};

return 0;

};

Теперь посмотрите на работу программы. Попробуем параметр --help и -h.

dron~# ./longopt2 --help

This is demo help. Try -h or --help.

option_index = 0 ("help",0,h)

dron~# ./longopt2 -h

This is demo help. Try -h or --help.

Segmentation fault

В первом случае все удачно, вывелась помощь и значение option_index. Во втором случае программа "упала" с сообщением об ошибке. Почему ? Ошибка Segmentation fault выдается когда программа пытается работать с неверными указателями. А в нашем случае мы пытаемся получить по option_index название параметра. В случае когда найден короткий параметр значение option_index не определено. Что же делать ?! А все просто. Модифицируем код чуток:

int option_index=-1; //обнулим в начале (установим признак ошибки)

while (...){

switch(...){

};

option_index = -1; // снова делаем ошибку

};

При такой работе option_index можно применять для определения типа переданного параметра. Если он был длинным, то это значение будет больше нуля и равно порядковому номеру параметра в массиве. Если же -1, то это значит, что параметр короткий:

if (option_index<0)

printf("short help option\n");

else

printf("option_index = %d (\"%s\",%d,%c)\n",

option_index,

long_options[option_index].name,

long_options[option_index].has_arg,

long_options[option_index].val

);

Теперь все работает:

dron~# ./longopt2 --help

This is demo help. Try -h or --help.

option_index = 0 ("help",0,h)

dron~# ./longopt2 -h

This is demo help. Try -h or --help.

short help option

Но это еще не все фокусы :) Попробуйте поиграть с параметрами size и file:

dron~# ./longopt2 -s 10

found size without value

dron~# ./longopt2 -s10

found size with value 10

dron~# ./longopt2 --size 10

found size without value

dron~# ./longopt2 --size=10

found size with value 10

dron~# ./longopt2 -f asd

file = asd

dron~# ./longopt2 -fasd

file = asd

dron~# ./longopt2 --file asd

file = asd

dron~# ./longopt2 --file=asd

file = asd

Как видите не все так просто. У нас size задан в массиве как optional_argument, т.е. параметр с опциональным аргументом. И получается, что когда нет явного признака присвоения, т.е. когда в коротком виде значение не стоит рядом с названием, а в длинном виде нет знака "=", наш параметр не получает никакого значения.

А вот параметр file заданный как required_argument получает свое значение в любом случае. И в этом заключается отличие типов аргументов. Теперь при разработке программ надо всегда учитывать, что если вы используете опциональный аргумент, то пользователь может ошибочно ввести командную строку, и вместо значения введенного пользователем Ваша программа будет использовать значение, которое вы используете по умолчанию. Незнаю, помоему достаточно значительная проблема, о которой следует помнить.

Осталось лишь сказать о функции getopt_long_only( ), которая является полным аналогом getopt_long() за исключением того, что даже короткие параметры она пытается сравнить с длинными. Модифицируйте название функции и попробуйте запустить программу:

dron~# ./longopt3 -f 10

file = 10

dron~# ./longopt3 -f10

file = 10

dron~# ./longopt3 --file=10

file = 10

dron~# ./longopt3 -file 10

file = 10

dron~# ./longopt3 -fil 100

file = 100

Вот так вот. Данная возможность может являться и плюсом и минусом. Полезной она оказывается, когда пользователь ошибочно набирает строку и указывает вместо --opt параметр -opt. Но эта функция может сыграть злую шутку, если вдруг из коротких названий параметров получится название длинного параметра и вместо перечисления '-size' = '-s -i -z -e' у Вас получится название длинного параметра --size от -s. Вообще работа данной функции несколько загадочна и неоднозначна, поэтому сами попробуйте ее в действии. Я же думаю, что в программах со сложными входными параметрами лучше воздержаться от ее использования. Зато в программах с небольшим количеством параметров эта функция может позволить игнорировать ошибки пользователя.

Будьте бдительны и Ваши программы будут работать без ошибок !!!

Шаг 12 - Вывод сообщений об ошибках программы

Сообщения об ошибках один из методов анализа правильности работы программы. Причем это не только сообщения для пользователя, но и сообщения об ошибках на этапе отладки приложения при разработке.

Вывод сообщений обычно делается через стандартный поток stderr, который перенаправляет все данные на консоль. Можно делать это и через stdout, но он может быть перенаправлен в файл или куда-либо еще, к тому же он обладает буфером, который ему не позволяет выводить данные моментально и если программа резко "обрушится", то Вы можете не увидеть нужных сообщений вообще. В потоке stderr буфер отключен, поэтому вызов функции fflush(stderr) не обязателен.

Все это конечно хорошо, если Вы разрабытаваете пользовательское консольное приложение, но как быть, если Ваша программа является сетевым приложением, например POP3 или каким-нибудь другим сервером. В этом случае работа с stderr не возможна, и отладка мягко выражаясь может сильно усложниться. Но не все так печально. Для этих случаев существует демон сообщений syslogd, который все сообщения от ядра и программ записывает в файлы хранящиеся в папке /var/log.

Для того, чтобы начать работу с этим демоном надо подключить файл syslog.h и вам станут доступными три процедуры:

#include <syslog.h>

void openlog( char *ident, int option, int facility)

void syslog( int priority, char *format, ...)

void closelog( void )

Предназначение функции closelog() думаю ясно, она закрывает дескриптор, который использовался для передачи сообщений в system logger. Ее использование опционально и, если вы забудете ее вызвать, то ничего катастрофичного не произойдет.

Для начала вывода сообщений Вам придется передать в функцию openlog() несколько необходимых параметров:

  • ident - текстовый идентификатор программы, обычно ее название. Он добавляется к началу каждого сообщения для того, чтобы было видно от какой программы поступают сообщения.

  • option - установки открываемого соединения, которые посредством операции OR могут складываться из следующих:

    • LOG_CONS - вывод напрямую в системную консоль, если вдруг происходит ошибка во время отправления сообщения

    • LOG_NDELAY - открывать соединение сразу, обычно соединение открывается после появления первого сообщения

    • LOG_PERROR - выводить сообщения в stderr

    • LOG_PID - добавлять PID программы в каждое сообщение. Полезно когда может работать одновременно несколько одинаковых программ, в этом случае их можно различить по идентификатору процесса.

  • facility - позволяет задать тип программы, которая выводит сообщение. Это полезно для того, чтобы разделять сообщения от различных программ и записывать их в разные файлы. Все это настраивается для syslogd файлом конфигурации /etc/syslog.conf. А значения этого параметра могут быть следующими:

    • LOG_AUTH - сообщения безопасности/авторизации (рекомендуется использовать LOG_AUTHPRIV)

    • LOG_AUTHPRIV - приватные сообщения безопасности/авторизации

    • LOG_CRON - сообщения от демонов времени (например, cron или at)

    • LOG_DAEMON - сообщения от других демонов системы

    • LOG_KERN - сообщения ядра системы

    • LOG_LOCAL0...LOG_LOCAL7 - зарезервированы для локального использования

    • LOG_LPR - подсистема принтера

    • LOG_MAIL - почтовая подсистема

    • LOG_NEWS - подсистема новостей USENET

    • LOG_SYSLOG - внутренние сообщения сгенерированные syslogd

    • LOG_USER (по умолчанию) - сообщения пользовательского уровня

    • LOG_UUCP - сообщения системы UUCP

Вызов функции openlog() также не обязателен, она будет автоматически вызвана при необходимости во время использования syslog(), но идентификатор программы будет установлен в NULL, что я думаю не будет считаться хорошим тоном.

Ну, и чтобы выводить сами сообщения надо использовать функцию syslog(), работа с которой похожа на работу с функцией printf, за исключением того, что сообщению можно задать приоритет(или тип), т.е. его важность. Задается приоритет параметром priority, который может иметь следующие значения:

  • LOG_EMERG - система не работает, грубо говоря в обмороке и требует госпитализации :)

  • LOG_ALERT - необходимо немедленно принять меры

  • LOG_CRIT - критическое состояние

  • LOG_ERR - ошибочное состояние

  • LOG_WARNING - состояние предупреждения

  • LOG_NOTICE - нормальное, но значимое, состояние

  • LOG_INFO - информационное сообщение

  • LOG_DEBUG - сообщение отладки, то что как раз нужно при разработке

А теперь попробуем написать программу test.c, использующую syslog:

#include <stdlib.h>

#include <stdio.h>

#include <syslog.h>

#define DEBUG

int main(){

int i=0;

openlog("test",LOG_PID,LOG_USER);

#ifdef DEBUG

syslog(LOG_DEBUG,"try to sending 10 messages");

#endif

for (i=0;i<10;i++){

syslog(LOG_INFO,"info message [i = %d] ",i);

};

#ifdef DEBUG

syslog(LOG_DEBUG,"try log to stderr");

#endif

closelog();

openlog("test_stderr",LOG_PERROR | LOG_PID,LOG_USER);

syslog(LOG_INFO,"this is attempt to use stderr for syslog");

closelog();

return 0;

};

Компилируем программу и попробуем запустить:

dron~# ./test

test_stderr[6222]: this is attempt to use stderr for syslog

Теперь можем зайти в файл /var/log/messages и посмотреть, что там получилось. А получилось вот что:

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 0]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 1]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 2]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 3]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 4]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 5]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 6]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 7]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 8]

Dec 20 11:25:04 dron-linux test[6222]: info message [i = 9]

Dec 20 11:25:04 dron-linux test_stderr[6222]: this is attempt to use stderr for syslog

Помоему классно, но почему-то не хватает некоторых сообщений. Посмотрим /var/log/debug и увидим, что все на месте :)

Dec 20 11:25:04 dron-linux test[6222]: try to sending 10 messages

Dec 20 11:25:04 dron-linux test[6222]: try log to stderr

То есть тут мы можем увидеть, что сообщения разных типов выводятся в разные файлы. Настроить это можно с помощью файла /etc/syslog.conf. К примеру в данном случае он выглядит вот так:

# /etc/syslog.conf

# For info about the format of this file, see "man syslog.conf"

*.=info;*.=notice /var/log/messages

*.=debug /var/log/debug

*.err /var/log/syslog

Обратите также внимание, что сообщения, которые должны были выводиться в stderr дублируются в файл. Т.е. по большому счету, даже если поток stderr будет не доступен, то сообщения программы все равно дойдут до вас и это очень хорошо.