Это gensio (произносится как gen'-see-oh), платформа для обеспечения согласованного представления различных типов потокового (и пакетного) ввода-вывода. Вы создаете объект gensio (или gensio) и можете использовать этот gensio, не зная слишком много о том, что происходит под ним. Вы можете установить gensio поверх другого, чтобы добавить функциональность протокола. Например, вы можете создать gensio TCP, наложить поверх него SSL и поверх него наложить Telnet. Он поддерживает ряд сетевых входов/выходов и последовательных портов. Он также поддерживает звуковые интерфейсы. Генсио, которые суммируются с другими генсио, называются фильтрами.
То же самое можно сделать и с принимающими портами. Вы можете настроить приемник gensio для приема соединений в стеке. Итак, в нашем предыдущем примере вы можете настроить TCP для прослушивания определенного порта и автоматически накладывать SSL и Telnet сверху при установлении соединения, и вы не будете уведомлены, пока все не будет готово.
gensio работает в Linux, BSD, MacOS и Windows. В Windows он предоставляет однопоточный (но также многопоточный) управляемый событиями интерфейс (с доступными блокирующими интерфейсами) для упрощения программирования с большим количеством операций ввода-вывода. Это во многом упрощает написание переносимого кода, управляемого вводом-выводом.
Очень важная особенность gensio заключается в том, что она значительно упрощает установку зашифрованных и аутентифицированных соединений, чем без нее. Помимо базового управления ключами, это на самом деле не сложнее, чем TCP или что-то еще. Он предлагает расширенную гибкость для управления процессом аутентификации, если это необходимо. Его действительно легко использовать.
Обратите внимание, что на странице руководства gensio(5) содержится более подробная информация об отдельных типах gensio.
Инструкции по сборке из исходного кода см. в конце раздела «Сборка».
Доступно несколько инструментов, которые используют gensios как в качестве примера, так и для тестирования. Это:
Демон, похожий на sshd, который использует certauth, ssl и SCTP или TCP-генсио для установления соединений. Он использует стандартную аутентификацию PAM и использует ptys. Подробности смотрите в gtlsshd(8).
В FAQ.rst есть пункт под названием «Как запустить gtlsshd в Windows», см. его и раздел «Создание на Windows» ниже для более подробной информации, так как есть несколько сложных вещей, с которыми вам придется справиться.
В библиотеке доступны следующие генсио:
Приемник gensio, который принимает строку стека gensio в качестве параметра. Это позволяет вам использовать gensio в качестве приемника. Когда conacc запускается, он открывает gensio, а когда gensio открывается, он сообщает получателю о новом дочернем элементе. Когда дочерний элемент закрывается, он пытается снова открыть дочерний элемент и повторить процесс (если только принятие не было отключено в conacc).
Почему вы хотите это использовать? Скажем, в ser2net вы хотите соединить один последовательный порт с другим. У вас может быть такое соединение:
connection : &con0
accepter : conacc,serialdev,/dev/ttyS1,115200
connector : serialdev,/dev/ttyS2,115200
И это соединит /dev/ttyS1 с /dev/ttyS2. Без conacc вы не могли бы использовать Serialdev в качестве приемника. Это также позволит вам использовать gtlsshd на последовательном порту, если вы хотите зашифровать аутентифицированный вход в систему через последовательный порт. Если вы запустили gtlsshd со следующим:
gtlsshd --notcp --nosctp --oneshot --nodaemon --other_acc
' conacc,relpkt(mode=server),msgdelim,/dev/ttyUSB1,115200n81 '
Вы можете связаться с:
gtlssh --transport ' relpkt,msgdelim,/dev/ttyUSB2,115200n81 ' USB2
Это создает надежную передачу пакетов через последовательный порт. Mode=server необходим для запуска relpkt в качестве сервера, поскольку обычно он работает как клиент, поскольку он не запускается как принимающий. Генсио SSL (который работает через транспорт) требует надежной связи, поэтому он не будет работать напрямую через последовательный порт.
Да, это похоже на набор букв.
Фильтр gensio, который устанавливается поверх звукового gensio и выполняет модемную манипуляцию со сдвигом звуковой частоты, как это используется в любительском радио AX.25.
Любительский радиопротокол для пакетной радиосвязи. Чтобы полностью использовать это, вам нужно будет написать код, поскольку он использует каналы и данные oob для ненумерованной информации, но вы можете делать базовые вещи, используя только gensiot, если все, что вам нужно, это один канал связи. Например, если вы хотите поговорить с кем-то по радио, а порт поцелуя на обеих машинах — 8001, на принимающей машине вы можете запустить:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),kiss,conacc,tcp,localhost,8001 '
который подключится к TNC и будет ждать соединения по адресу AE5KM-1. Тогда вы можете запустить:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"),kiss,tcp,localhost,8001 '
на другой машине. Это позволит подключиться к другой машине через TNC 0 с данным адресом. Тогда все, что вы вводите в одном, будет отображаться в другом, построчно. Введите «Ctrl-D», чтобы выйти. Часть «stdio(self)» отключает необработанный режим, поэтому это построчно, и вы получаете локальное эхо. В противном случае каждый вводимый вами символ будет отправлять пакет, и вы не сможете видеть, что печатаете.
Чтобы подключиться к системе N5COR-11 AX.25 BBS, вам необходимо сделать:
gensiot -i ' xlt(nlcr),stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,N5COR-11,AE5KM-2"),kiss,tcp,localhost,8001 '
Большинство систем BBS используют для новой строки CR, а не NL, поэтому для перевода входящих этих символов используется xlt gensio.
Конечно, поскольку это gensio, вы можете поместить под ax25 любой работоспособный gensio, какой захотите. Итак, если вы хотите поиграть или протестировать без радио, вы можете использовать ax25 через многоадресную рассылку UDP. Вот принимающая сторона:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),conacc, '
' udp(mcast="ipv4,224.0.0.20",laddr="ipv4,1234",nocon), '
' ipv4,224.0.0.20,1234 '
а вот сторона разъема:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"), '
' udp(mcast="ipv4,224.0.0.20",laddr="ipv4,1234",nocon), '
' ipv4,224.0.0.20,1234 '
поцелуй не требуется, поскольку UDP уже является пакетно-ориентированным носителем. Или вы можете использовать программу greflector для создания моделируемой радиоситуации. На машине «radiopi2» запустите:
greflector kiss,tcp,1234
который создаст программу, которая будет отражать все полученные входные данные для всех других соединений. Затем на принимающей стороне:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),kiss,conacc,tcp,radiopi2,1234 '
и соединительная сторона:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"),kiss,tcp,radiopi2,1234 '
В тестовом коде для некоторых тестов используется отражатель, поскольку его очень удобно использовать.
Все это подробно описано в gensio(5). Если не указано иное, все они доступны в качестве акцепторов или соединительных генераторов.
Вы можете создавать свои собственные генсио, регистрировать их в библиотеке и объединять их вместе с другими генсио.
Самый простой способ сделать это — украсть код из gensio, который делает то, что вы хотите, а затем изменить его, чтобы создать свой собственный gensio. К сожалению, нет хорошей документации о том, как это сделать.
Включаемый файл include/gensio/gensio_class.h содержит интерфейс между основной библиотекой gensio и самой gensio. Все вызовы gensio проходят через одну функцию с номерами, обозначающими запрашиваемую функцию. Вы должны сопоставить все это с реальными операциями. Это несколько болезненно, но значительно упрощает прямую и обратную совместимость.
Создать свой собственный генсио таким способом довольно сложно. Государственная машина для чего-то подобного может быть на удивление сложной. Уборка – это самое сложное. Вы должны убедиться, что у вас нет всех обратных вызовов и что никакие таймеры не могут быть вызваны обратно в состоянии гонки при завершении работы. Только самые простые генераторы (echo, dummy), странные генераторы (conadd, Keepopen, stdio) и генераторы, имеющие каналы (mux, ax25), реализуют интерфейс напрямую. Все остальное использует include/gensio/gensio_base.h. gensio_base предоставляет базовый конечный автомат для gensio. Он имеет часть фильтра (которая не является обязательной) и часть низкого уровня (ll), которая не является таковой.
Через интерфейс фильтра проходят данные для обработки. Это используется для таких вещей, как SSL, certauth, предел скорости и т. д. Фильтрующие генераторы будут использовать это. Все они используют gensio_ll_gensio (для наложения одного генсио поверх другого генсио) для ll.
У каждого терминального генсио есть свой собственный фильтр и вообще нет фильтра. Для ll, основанных на файловом дескрипторе (fd), используется gensio_ll_fd. Также есть ll для IPMI Serial-over-LAN (ipmisol) и для звука. Очевидно, что большинство терминальных устройств (tcp, udp, sctp, последовательный порт, pty) используют fd ll.
Если у вас есть gensio, вы можете скомпилировать его как модуль и вставить в $(moduleinstalldir)/<version>. Тогда генсио просто возьмет его и использует. Вы также можете связать его со своим приложением и выполнить функцию инициализации из своего приложения.
gensio mdns уже обсуждался, но библиотека gensio предоставляет простой в использовании интерфейс mDNS. Включаемый файл для него находится в gensio_mdns.h, и вы можете использовать справочную страницу gensio_mdns(3), чтобы получить дополнительную информацию о нем.
Чтобы установить соединение mdns с помощью gensiot, скажем, у вас настроен ser2net с включенным mdns, например:
connection : &my-port
accepter : telnet(rfc2217),tcp,3001
connector : serialdev,/dev/ttyUSB1,115200N81
options :
mdns : true
то вы можете подключиться к нему с помощью gensiot:
gensiot ' mdns,my-port '
gensiot найдет сервер, порт, а также то, включены ли telnet и rfc2217, и установит соединение.
Кроме того, существует инструмент gmdns, который позволяет выполнять запросы и рекламу, а gtlssh может выполнять запросы mDNS для поиска сервисов. Если у вас есть безопасные авторизованные входы в систему ser2net и вы включили mdns в ser2net, например:
connection : &access-console
accepter : telnet(rfc2217),mux,certauth(),ssl,tcp,3001
connector : serialdev,/dev/ttyUSBaccess,115200N81
options :
mdns : true
это делает настройку очень удобной, так как вы можете просто сделать:
gtlssh -m access-console
Правильно, вы можете просто использовать имя соединения напрямую, не нужно знать хост, включен ли telnet или rfc2217 или какой порт. Разумеется, вам все равно придется настроить ключи и все такое на сервере ser2net в соответствии с этими инструкциями.
gensio имеет объектно-ориентированный интерфейс, управляемый событиями. Также доступны синхронные интерфейсы. В gensio вы имеете дело с двумя основными объектами: gensio и приемником gensio. Gensio предоставляет интерфейс связи, с помощью которого вы можете подключаться, отключаться, записывать, получать и т. д.
Приемник gensio позволяет принимать входящие соединения. Если соединение появляется, оно дает вам gensio.
Интерфейс управляется событиями, поскольку по большей части он полностью неблокируется. Если вы открываете gensio, вы передаете ему обратный вызов, который будет вызван при установлении соединения или при сбое соединения. То же самое и с близкими. Операция записи вернет количество принятых байтов, но она может не занять все байты (или даже какой-либо из байтов), и вызывающая сторона должна это учитывать.
Для удобства открытый и закрытый интерфейсы имеют дополнительный интерфейс блокировки. Они заканчиваются на _s. Это сделано для удобства, но в этом нет необходимости, и использовать их следует осторожно, поскольку их невозможно использовать из обратных вызовов.
Говоря об обратных вызовах, данные и информация, поступающие от gensio пользователю, выполняются с помощью обратного вызова функции. Чтение данных, и когда gensio готов к записи, данные возвращаются в обратном вызове. Аналогичный интерфейс используется для обращения пользователя к уровню gensio, но он скрыт от пользователя. Этот тип интерфейса легко расширяется, можно легко добавлять новые операции, не нарушая старые интерфейсы.
Библиотека предоставляет несколько способов создания gensio или приемника gensio. Основной способ — str_to_gensio() и str_to_gensio_accepter(). Они предоставляют возможность указать стек gensio или акцепторов в виде строки и построить. В общем, вам следует использовать этот интерфейс, если можете.
Как правило, интерфейсы, не чувствительные к производительности, основаны на строках. Вы увидите это в gensio_control и во вспомогательных данных в интерфейсе чтения и записи для управления определенными аспектами записи.
Библиотека также предоставляет способы настройки ваших gensio путем индивидуального создания каждого из них. В некоторых ситуациях это может быть необходимо, но это ограничивает возможность использования новых функций библиотеки gensio по мере ее расширения.
Если gensio поддерживает несколько потоков (например, SCTP), номера потоков передаются в auxdata с помощью «stream=n». Потоки не управляются индивидуально.
Каналы, с другой стороны, представляют собой отдельные потоки данных по одному и тому же соединению. Каналы представлены как отдельные генсио, и их поток можно контролировать индивидуально.
Есть несколько включаемых файлов, с которыми вам может понадобиться иметь дело при использовании gensios:
По большей части они описаны на страницах руководства.
Для создания собственных gensio вам доступны следующие включаемые файлы:
Каждый включаемый файл содержит множество документации об отдельных вызовах и обработчиках.
gensio имеет собственный набор ошибок, позволяющий абстрагировать его от ошибок ОС (называемый GE_xxx) и обеспечить большую гибкость при составлении отчетов об ошибках. Они находятся во включаемом файле gensio_err.h (автоматически включается из gensio.h) и могут быть преобразованы из чисел в значимую строку с помощью gensio_err_to_str(). Ноль определяется как не ошибка.
Если возникает нераспознанная ошибка операционной системы, возвращается GE_OSERR, а журнал передается через интерфейс журнала обработчика ОС.
Одна немного раздражающая особенность gensio заключается в том, что вам необходимо предоставить обработчик ОС (struct gensio_os_funcs) для обработки функций типа ОС, таких как распределение памяти, мьютексы, возможность обработки файловых дескрипторов, таймеров и времени, а также некоторых других вещей.
Библиотека предоставляет несколько обработчиков ОС. Вы можете вызвать gensio_alloc_os_funcs(), чтобы выделить значение по умолчанию для вашей системы (POSIX или Windows). Вы можете увидеть эту справочную страницу для получения более подробной информации. Как правило, это будет наиболее эффективный вариант для вашей системы.
Для систем POSIX доступны обработчики ОС для glib и TCL, выделяемые с помощью gensio_glib_funcs_alloc() и gensio_tcl_funcs_alloc(). На самом деле они работают не очень хорошо, особенно с точки зрения производительности: API-интерфейсы для glib и TCL не очень хорошо подходят для того, что делает gensio. TCL может поддерживать только однопоточную работу. Многопоточная операция glib имеет только один поток, ожидающий ввода-вывода. Но они работают, и тесты проводятся с ними. Они недоступны в Windows из-за плохих абстракций в glib и отсутствия мотивации в TCL.
Но если вы используете что-то еще, например X Windows и т. д., имеющее собственный цикл событий, вам может потребоваться адаптировать его для своих нужд. Но хорошо то, что вы можете сделать это и интегрировать gensio практически с чем угодно.
Существует также интерфейс ожидания, который обеспечивает удобный способ ожидания событий во время выполнения цикла событий. Именно так вы обычно входите в цикл событий, поскольку он обеспечивает удобный способ сигнализировать о том, что вы закончили и вам нужно выйти из цикла.
Документация для этого находится в:
включить/gensio/gensio_os_funcs.h
Библиотека gensio полностью поддерживает потоки и полностью потокобезопасна. Однако он использует сигналы в системе POSIX и COM в системах Windows, поэтому требуется некоторая настройка.
«Основной» поток должен вызывать gensio_os_proc_setup() при запуске и вызывать gensio_os_proc_cleanup() после его завершения. Это настраивает сигналы и обработчики сигналов, локальное хранилище потоков в Windows и другие вещи.
Вы можете создавать новые потоки из уже настроенного потока с помощью gensio_os_new_thread(). Это дает вам базовый поток ОС и правильно настроен для gensio.
Если у вас есть поток, созданный другими способами, который вы хотите использовать в gensio, при условии, что поток создает другой поток и не выполняет никаких блокирующих функций (любое ожидание, фоновая обработка, функции, которые заканчиваются на _s, например read_s, и т. д.), вам не нужно их настраивать. Таким образом, какой-то внешний поток может записывать данные, пробуждать другой поток или делать что-то подобное.
Если внешний поток должен выполнить эти действия, он должен вызвать gensio_os_thread_setup().
Как упоминалось в разделе «Потоки», библиотека gensio в Unix использует сигналы для пробуждения между потоками. Я внимательно посмотрел, и другого способа сделать это чисто нет. Но в Windows тоже есть несколько функций, похожих на сигналы, и они также доступны в gensio.
Если вы используете gensio_alloc_os_funcs(), вы получите функции ОС, используя переданный сигнал для IPC. Вы можете передать GENSIO_OS_FUNCS_DEFAULT_THREAD_SIGNAL для сигнала, если хотите использовать значение по умолчанию, то есть SIGUSR1. Используемый вами сигнал будет заблокирован и принят gensio, вы не сможете его использовать.
gensio также обеспечивает некоторую общую обработку нескольких сигналов. В Unix он будет обрабатывать SIGHUP через функцию gensio_os_proc_register_reload_handler().
В Windows и Unix вы можете использовать gensio_os_proce_register_term_handler(), который будет обрабатывать запросы на завершение (SIGINT, SIGTERM, SIGQUIT в Unix) и gensio_os_proc_register_winsize_handler() (SIGWINCH в Unix). То, как они проникают через Windows, немного сложнее, но оно незаметно для пользователя.
Все обратные вызовы из ожидания подпрограммы ожидания, а не из обработчика сигнала. Это должно значительно упростить вашу жизнь.
Более подробную информацию обо всем этом можно найти на страницах руководства.
Чтобы создать gensio, общий способ сделать это — вызвать str_to_gensio()
с правильно отформатированной строкой. Строка форматируется так:
<тип>[([<опция>[,<опция[...]]])][,<тип>...][,<конечная опция>[,<конечная опция>]]
end option
предназначен для терминальных генсио или тех, которые находятся в нижней части стека. Например, tcp,localhost,3001
создаст gensio, который подключается к порту 3001 на локальном хосте. Для последовательного порта, например, serialdev,/dev/ttyS0,9600N81
создаст соединение с последовательным портом /dev/ttyS0.
Это позволяет накладывать слои gensio поверх слоев gensio. Например, чтобы наложить telnet поверх TCP-соединения:
telnet,tcp,localhost,3001
Допустим, вы хотите включить RFC2217 в своем telnet-соединении. Вы можете добавить такую возможность:
telnet(rfc2217=true),tcp,localhost,3001
Когда вы создаете gensio, вы предоставляете обратный вызов с пользовательскими данными. Когда в gensio происходят события, будет вызван обратный вызов, чтобы пользователь мог их обработать.
Приемник gensio аналогичен соединительному gensio, но вместо него используется str_to_gensio_accepter()
. Формат тот же. Например:
telnet(rfc2217=true),tcp,3001
создаст TCP-приемник с telnet сверху. Принимающим сторонам обычно не нужно указывать имя хоста, если вы хотите привязаться ко всем интерфейсам на локальном компьютере.
После того как вы создали gensio, он еще не открыт и не работает. Чтобы использовать его, вам придется его открыть. Чтобы открыть его, выполните:
struct gensio * io ;
int rv ;
rv = str_to_gensio ( "tcp,localhost,3001" , oshnd ,
tcpcb , mydata , & io );
if ( rv ) { handle error }
rv = gensio_open ( io , tcp_open_done , mydata );
if ( rv ) { handle error }
Обратите внимание, что когда gensio_open()
возвращает значение, gensio не открывается. Вам придется подождать, пока не будет вызван обратный вызов (в данном случае tcp_open_done()
). После этого вы можете использовать его.
Как только gensio откроется, вы не получите сразу никаких данных, поскольку получение отключено. Вы должны вызвать gensio_set_read_callback_enable()
чтобы включить или выключить, будет ли обратный вызов (в данном случае tcpcb
) вызываться при получении данных.
При вызове обработчика чтения передаются буфер и длина. Вам не обязательно обрабатывать все данные, если вы не можете. Вы должны обновить buflen, указав количество байтов, которые вы фактически обработали. Если вы не обрабатываете данные, необработанные данные будут буферизованы в gensio для дальнейшего использования. Это не значит, что если вы не обрабатываете все данные, вам следует отключить разрешение чтения, иначе событие будет немедленно вызвано снова.
Если в соединении что-то идет не так, обработчик чтения вызывается с набором ошибок. buf
и buflen
в этом случае будут иметь значение NULL.
Для записи вы можете вызвать gensio_write()
для записи данных. Вы можете использовать gensio_write()
в любое время в открытом файле gensio. gensio_write()
может не принять все данные, которые вы в него записываете. Параметр count
передает обратно количество байтов, фактически взятых при вызове записи.
Вы можете разработать свой код для вызова gensio_set_write_callback_enable()
когда у вас есть данные для отправки, и gensio вызовет обратный вызов готовности к записи, и вы сможете писать из обратного вызова. Обычно это проще, но включение и отключение обратного вызова записи добавляет некоторые накладные расходы.
Более эффективный подход — записывать данные всякий раз, когда вам нужно, и отключать обратный вызов записи. Если операция записи возвращает меньше, чем полный запрос, на другом конце используется управление потоком, и вам следует включить обратный вызов записи и дождаться его вызова, прежде чем отправлять больше данных.
В обратных вызовах вы можете получить пользовательские данные, которые вы передали в вызов создания, с помощью gensio_get_user_data()
.
Обратите внимание: если вы открываете, а затем немедленно закрываете gensio, это нормально, даже если обратный вызов открытия не был вызван. Однако в этом случае обратный обратный вызов open может вызываться, а может и не вызываться, поэтому может быть сложно обработать это должным образом.
Вы можете выполнять базовый синхронный ввод-вывод с помощью gensios. Это полезно в некоторых ситуациях, когда вам нужно прочитать что-то встроенное. Для этого позвоните:
err = gensio_set_sync ( io );
Данный gensio перестанет доставлять события чтения и записи. Другие события доставляются . Тогда вы можете сделать:
err = gensio_read_s ( io , & count , data , datalen , & timeout );
err = gensio_write_s ( io , & count , data , datalen , & timeout );
Count устанавливается на фактическое количество прочитанных/записанных байтов. Если вам все равно, оно может быть NULL (хотя для чтения это не имеет особого смысла).
Тайм-аут может быть NULL, если это так, то ждите вечно. Если вы установите тайм-аут, он будет обновлен до оставшегося времени.
Обратите внимание, что сигналы заставят их немедленно вернуться, но об ошибке не будет сообщено.
Чтение будет блокироваться до тех пор, пока не поступят какие-то данные и не вернут эти данные. Он не ждет, пока буфер заполнится. timeout — это временной параметр, чтение будет ждать определенное количество времени, пока чтение не завершится и не вернется. Тайм-аут не является ошибкой, счетчик просто обнуляется.
Записывает блок до тех пор, пока не будет записан весь буфер или не истечет время ожидания. Опять же, тайм-аут не является ошибкой, общее количество фактически записанных байт возвращается в счетчике.
Как только вы закончите синхронный ввод-вывод с помощью gensio, вызовите:
err = gensio_clear_sync ( io );
и доставка через интерфейс событий продолжится, как и раньше. При вызове вы не должны находиться в синхронном вызове чтения или записи, результаты будут неопределенными.
Обратите внимание, что другие операции ввода-вывода на других устройствах Gensio по-прежнему будут выполняться при ожидании синхронного ввода-вывода.
В настоящее время нет способа дождаться нескольких генераторов с синхронным вводом-выводом. Если вы это делаете, вам следует просто использовать управляемый событиями ввод-вывод. Это более эффективно, и в конечном итоге вы все равно сделаете то же самое.
Как и gensio, акцептор gensio не работает, когда вы его создаете. Вы должны вызвать gensio_acc_startup()
чтобы включить его:
struct gensio_accepter * acc ;
int rv ;
rv = str_to_gensio_accepter ( "tcp,3001" , oshnd ,
tcpacccb , mydata , & acc );
if ( rv ) { handle error }
rv = gensio_startup ( acc );
if ( rv ) { handle error }
Обратите внимание, что для вызова запуска не существует обратного вызова, чтобы узнать, когда он включен, потому что нет реальной необходимости знать, потому что вы не можете писать в него, он выполняет только обратные вызовы.
Даже после запуска приемника он все равно ничего не сделает, пока вы не вызовете gensio_acc_set_accept_callback_enable()
чтобы включить этот обратный вызов.
Когда вызывается обратный вызов, он дает вам gensio в параметре data
, который уже открыт с отключенным чтением. Генсио, полученное от приемника генсио, может иметь некоторые ограничения. Например, вы не сможете закрыть, а затем снова открыть его.
Принимающие устройства gensio могут выполнять синхронные подтверждения с помощью gensio_acc_set_sync()
и gensio_acc_accept_s
. Подробности смотрите на справочных страницах.
struct gensio_os_funcs
имеет обратный вызов vlog для обработки внутренних журналов gensio. Они вызываются, когда происходит что-то важное, но gensio не имеет возможности сообщить об ошибке. Его также можно вызвать, чтобы облегчить диагностику проблемы, когда что-то идет не так.
Каждый из классов gensio и gensio Accepter имеет подклассы для обработки последовательного ввода-вывода и установки всех параметров, связанных с последовательным портом.
Вы можете узнать, является ли gensio (или любой из его дочерних элементов) последовательным портом, вызвав gensio_to_sergensio()
. Если это возвращает NULL, это не sergensio, и ни один из его дочерних элементов не является sergensio. Если он возвращает значение, отличное от NULL, он возвращает объект sergensio, который вы можете использовать. Обратите внимание, что gensio, возвращаемый sergensio_to_gensio()
будет тем же, который был передан в gensio_to_sergensio()
, а не обязательно тем gensio, с которым sergensio напрямую связан.
Sergensio может быть клиентом, что означает, что он может устанавливать параметры последовательного порта, или сервером, что означает, что он будет получать настройки последовательного порта с другого конца соединения.
Большинство sergensios являются только клиентскими: Serialdev (обычный последовательный порт), ipmisol и приёмник stdio. В настоящее время только telnet имеет возможности как клиента, так и сервера.
ПРИМЕЧАНИЕ. Описанный здесь интерфейс Python устарел. Используйте тот, который находится в c++/swig/pygensio сейчас.
Вы можете получить доступ практически ко всему интерфейсу gensio через Python, хотя это сделано немного иначе, чем интерфейс C.
Поскольку Python полностью объектно-ориентирован, gensios и gensio Accepters являются первоклассными объектами, наряду с gensio_os_funcs, sergensios и waiters.
Вот небольшая программа:
import gensio
class Logger :
def gensio_log ( self , level , log ):
print ( "***%s log: %s" % ( level , log ))
class GHandler :
def __init__ ( self , o , to_write ):
self . to_write = to_write
self . waiter = gensio . waiter ( o )
self . readlen = len ( to_write )
def read_callback ( self , io , err , buf , auxdata ):
if err :
print ( "Got error: " + err )
return 0
print ( "Got data: " + buf );
self . readlen -= len ( buf )
if self . readlen == 0 :
io . read_cb_enable ( False )
self . waiter . wake ()
return len ( buf )
def write_callback ( self , io ):
print ( "Write ready!" )
if self . to_write :
written = io . write ( self . to_write , None )
if ( written >= len ( self . to_write )):
self . to_write = None
io . write_cb_enable ( False )
else :
self . to_write = self . to_write [ written :]
else :
io . write_cb_enable ( False )
def open_done ( self , io , err ):
if err :
print ( "Open error: " + err );
self . waiter . wake ()
else :
print ( "Opened!" )
io . read_cb_enable ( True )
io . write_cb_enable ( True )
def wait ( self ):
self . waiter . wait_timeout ( 1 , 2000 )
o = gensio . alloc_gensio_selector ( Logger ())
h = GHandler ( o , "This is a test" )
g = gensio . gensio ( o , "telnet,tcp,localhost,2002" , h )
g . open ( h )
h . wait ()
Интерфейс представляет собой довольно прямой перевод интерфейса C. Представление интерфейса в Python находится в swig/python/gensiodoc.py, вы можете посмотреть его в документации.
Интерфейс C++ описан в c++/README.rst.
Новый интерфейс pygensio представляет собой более чистую реализацию, в которой используются директора swig вместо обратных вызовов, написанных вручную в Python. См. README.rst в c++/swig/pygensio. В каталогах glib и tcl также есть glib и tcl OS_Funcs.
Полный интерфейс C++ доступен программам Go через swig и swig-директора. Подробности см. в c++/swig/go/README.rst.
Это обычная система автоконф, ничего особенного. Обратите внимание: если вы получите это непосредственно из git, у вас не будет включенной инфраструктуры сборки. В основном каталоге есть скрипт с именем «reconf», который создаст его за вас.
Если вы не знаете об autoconf, в файле INSTALL есть некоторая информация, или погуглите.
Чтобы полностью собрать gensio, вам понадобится следующее:
Следующее устанавливает все, кроме openipmi, в Ubuntu 20.04:
- sudo apt install gcc g++ git swig python3-dev libssl-dev pkg-config
- libavahi-client-dev avahi-daemon libtool autoconf automake make libsctp-dev libpam-dev libwrap0-dev libglib2.0-dev tcl-dev libasound2-dev libudev-dev
В Redhat libwrap больше нет, поэтому вы не будете его использовать, а swig, похоже, недоступен, поэтому вам придется создать его самостоятельно, по крайней мере, с поддержкой Go и Python. Вот команда для Redhat-подобных систем:
- sudo yum install gcc gcc-c++ git python3-devel swig openssl-devel
- PKG-CONFIG AVAHI-DEVEVEL LIBTOOL AUTOCONF Automake Make LKSCTP-Tools-Devel Pam-Devel Glib2-Devel TCL-Devel Alsa-Lib-Devel Systemd-Devel
Возможно, вам придется сделать следующее, чтобы обеспечить доступ к пакетам разработки:
Sudo DNF Config-Manager-Devel с поддержкой SET
И получить модули ядра SCTP, вам, возможно, придется сделать:
Sudo yum установить ядра модулы-Экстра
Чтобы использовать язык GO, вы должны получить версию SWIG 4.1.0 или более. Возможно, вам придется вытащить версию с кровотечением из GIT и использовать ее.
Обработка конфигурации установки Python - это небольшая боль. По умолчанию сценарии сборки будут помещать его везде, где программа Python ожидает установленных программ Python. Обычный пользователь обычно не имеет доступа к записи к этому каталогу.
Чтобы переопределить это, вы можете использовать параметры настройки--with-withoninstall и--with-pythoninstalllib, или вы можете установить переменные среды Pythoninstalldir и Pythoninstalllibdir, куда вы хотите, чтобы библиотеки и модули переходили.
Обратите внимание, что вам, возможно, потребуется установить-с UCP-блокировкой на ваш LockDir (в более старых системах это/var/lock, что является по умолчанию. На более новом это может быть/запустить/блокировать/блокировать. Член групп на диатуете и блокировки, чтобы иметь возможность открывать последовательные устройства и/или блокировки.
Поддержка языка GO требуется установить и на пути.
Когда я продолжал добавлять Генсиос в библиотеку, например, крипто, MDN, звук, IPMI, SCTP и т. Д. Количество зависимостей в библиотеке выходило из -под контроля. Почему вы должны загружать Libasound или Libopenipmi, если вам это не нужно? Кроме того, хотя библиотека поддержала добавление собственного Gensios через программный API, у нее не было стандартного способа добавить их для системы, чтобы вы могли написать свой собственный gensio и позволить всем в системе использовать ее.
Библиотека Gensio поддерживает загрузку Gensios динамически или создает их в библиотеку. По умолчанию, если вы создаете общие библиотеки, то все Gensios собираются в виде модулей для динамической загрузки и установлены в месте, которое делает это возможным. Если вы не создаете общие библиотеки, все Gensios встроены в библиотеку. Но вы можете переопределить это поведение.
Чтобы установить все Gensios, чтобы встроить в библиотеку, вы можете добавить «-with-all-gensios = yes» в командной строке Configure, и он построит их в библиотеку.
Вы также можете настроить их для всех быть динамически загруженными, добавив «-с all-gensios = dynamic», но это по умолчанию.
Вы также можете отключить все gensios по умолчанию, указав «-с all-gensios = no». Тогда ни один Генсиос не будет построен по умолчанию. Это полезно, если вы хотите только несколько Gensios, вы можете выключить их все, а затем включить, которые вы хотите.
Чтобы установить, как строятся отдельные генсиос, вы делаете «-с- <gensio> = x», где x-это «нет (не строите), да (строить в библиотеку) или динамический (динамически загруженный исполняемый файл). Если вы только хотели построить TCP Gensio в библиотеку и сделать Rest Dynamic, вы можете настроить для всех динамических Gensios, а затем добавить «with-net = yes».
Эти модули по умолчанию помещаются в
Обратите внимание, что динамическая нагрузка всегда доступна, даже если вы создаете все Gensios в библиотеке. Таким образом, вы все равно можете добавить свой собственный gensios, добавив затем в правильный каталог.
Gensios будет загружен сначала из переменной среды LD_LIBRARY_PATH, затем из gensio_library_path, затем из местоположения по умолчанию.
MacOS, будучи своего рода * NIX, довольно чисто строится с HomeBrew (https://brew.sh). Вы должны, конечно, установить все необходимые вам библиотеки. Большинство все работает, со следующими исключениями:
* CM108GPIO * SCTP * UUCP блокировка
Встроенный код DNSSD используется для MDN, поэтому Avahi не требуется.
Заблокирование стада для последовательных портов работает, поэтому блокировка UUCP действительно не требуется.
Openipmi должен работать, но он не доступен в Homebrew, поэтому вам придется построить его самостоятельно.
Установите необходимое программное обеспечение:
- PKG Установка GCC Portaudio AutoConf Automake Libtool Mdnsresponder Swig
- Go Python3 Gmake
Вы должны использовать Gmake, чтобы скомпилировать его, по какой -то причине стандарт на BSD не принимает переменную «C ++» в списке требований. Следующее не работает и не составлено:
* SCTP * Ипмизол * CM108GPIO
Добавьте следующее в /etc/rc.conf:
mdnsd_enable = да
И перезагрузите или запустите сервис.
Pty Gensio проваливает Oomtest (oomtest 14), кажется, что -то с BSD Ptys. Я вижу 07 символ, вставленного в поток данных в случаях. Я не тратил на это слишком много времени, но, поскольку это тестируется на Linux и MacOS, я не думаю, что проблема в коде Генсио.
Библиотека Gensio может быть построена под Windows, используя Mingw64. Следующие вещи не работают:
* SCTP * Пэм * libwrap * Ипмизол
Вам также не нужно устанавливать ALSA, он использует звуковой интерфейс Windows для звука.
CM108GPIO использует собственные интерфейсы Windows, поэтому UDEV не требуется.
Используются встроенные интерфейсы Windows, поэтому вам не нужны Avahi или DNSSD. Вам нужно будет установить библиотеку PCRE, если вам нужны регулярные выражения в ней.
Вам нужно получить MSYS2 от https://msys2.org. Затем установите AutoConf, Automake, Libtool, Git, Make и Swig в качестве хоста инструментов:
Pacman -s AutoConf Automake Libtool Git Make Swig
Вы должны установить версию Mingw-W64-X86_64-XXX во всех библиотеках или версии Mingw-W64-I686-XXX всех библиотек. 32-битный не очень хорошо протестирован:
PACMAN -S MINGW-W64-X86_64-GCC mingw-w64-x86_64-python3 mingw-w64-x86_64-pcre mingw-w64-x86_64-openssl
для Mingw64 или для UCRT64:
PACMAN -S MINGW-W64-UCRT-X86_64-GCC mingw-w64-ucrt-x86_64-python3 mingw-w64-ucrt-x86_64-pcre mingw-w64-ucrt-x86_64-openssl
Для GO, установите go с https://go.dev, выходите в систему и войдите в систему. Затем он должен быть на пути, но если это не так, вам нужно будет добавить его в путь. Я не работал над Mingw32, но я не пробовал 32-разрядную версию GO.
Для gtlsshd -Sysconfdir не имеет никакого значения в Windows. Вместо этого, SysConf DIR относительно участка исполняемого, в ./etc/gtlssh. Так что, если gtlsshd в:
C:/программа файлов/gensio/bin/gtlsshd
Sysconfdir будет:
C:/программа файлов/gensio/etc/gtlssh
Для стандартной установки вы можете запустить:
../configure - -sbindir =/gensio/bin -libexecdir =/gensio/bin -Мандир =/gensio/man-includedir =/gensio/include -with-pythoninstall =/gensio/python3--prefix =/gensio
И когда вы запускаете «Сделайте установку destdir = ...», и вы установите Destdir туда, куда вы хотите, как «C:/Program Files». Затем вы можете добавить это в путь, используя панель управления. Чтобы использовать GTLSSHD, вы создаете каталог ETC/GTLSSHD в каталоге Gensio. Вы должны установить разрешения в этом каталоге, чтобы иметь доступ только к системе и администраторам, например:
PS C: Program Files (x86) gensio etc> icacls gtlssh gtlssh nt Authority System: (OI) (CI) (F) Встроенные Администраторы: (OI) (CI) (F)
В противном случае gtlsshd не удастся с ошибкой о разрешениях на ключ. Вы можете установить это разрешение на файл .Key вместо каталога, но вам придется устанавливать его снова каждый раз, когда вы генерируете новый ключ.
Для использования компилятора Inno Setup, сделайте «Сделайте установку destdir = $ home/install», а затем запустите Inno на gensio.iss. Он создаст исполняемый установщик для установки Gensio.
Затем вам нужно удалить файлы .la из каталога установки, поскольку они облажались с другими вещами:
rm $ home/install/gensio/lib/*. la
Есть ряд тестов для Gensios. Все они работают на Linux, если у вас есть модуль ядра SerialSim. Помимо серийных портов, они работают на других платформах, так как на этой платформе поддерживаются Gensios.
Тесты последовательного порта требуют модуля ядра SerialSim и интерфейса Python. Они находятся на https://github.com/cminyard/serialsim и позволяют тестам использовать смоделированный последовательный порт для чтения линии управления модемом, ошибок ввода и т. Д.
Вы можете обойтись без Serialsim, если у вас есть три последовательных устройства: одно подключен в режим Echo (RX и TX, связанные вместе) и два последовательных устройства, подключенные вместе Do I/O на одном устройстве переходят/исходят от другого. Это должно работать на платформах, не являющихся Linux. Затем установите следующие переменные среды:
export GENSIO_TEST_PIPE_DEVS= " /dev/ttyxxx:/dev/ttywww "
export GENSIO_TEST_ECHO_DEV= " /dev/ttyzzz "
Он не сможет проверить ModemState или RS485.
Они также требуют программы ipmi_sim из библиотеки Openipmi по адресу https://github.com/cminyard/openipmi для запуска тестов IPMisol.
Чтобы запустить тесты, вам необходимо включить некоторую внутреннюю отладку, чтобы получить полный эффект. Обычно вы хотите запустить что -то вроде:
./configure --enable-internal-trace CFLAGS= ' -g -Wall '
Вы также можете включить -O3 в CFLAGS, если хотите, но это усложняет отладку.
Есть два основных типа тестов. Тесты Python представляют собой функциональные тесты, тестирующие как интерфейс Python, так и библиотеку Gensio. В настоящее время они в порядке, но есть много возможностей для улучшения. Если вы хотите помочь, вы можете написать тесты.
Опендист раньше был тестером из памяти, но превратился в нечто более обширное. Он порождает программу gensiot с конкретными переменными среды, чтобы она не удалась в определенные точки, а также для утечки памяти и других проверок памяти. Он записывает данные Gensiot через свой Stdin и получает данные о Stdout. Некоторые тесты (например, Serialdev) используют эхо. Другие тесты создают отдельное соединение по сети, а данные о потоках данных как в Stdin, так и возвращаются через отдельное соединение, и течет в отдельное соединение и возвращается через STDOUT. Oomtest многопоточный, и количество потоков можно контролировать. Oomtest нашел много ошибок. У него много ручек, но вы должны посмотреть на исходный код для параметров. Это должно быть задокументировано, если кто -то захочет стать волонтером ...
Для настройки для отходов, установите AFL, затем настройте со следующим:
mkdir Zfuzz ; cd Zfuzz
../configure --enable-internal-trace=yes --disable-shared --with-go=no
CC=afl-gcc CXX=afl-g++
Или используйте Clang, если доступно:
../configure --enable-internal-trace=yes --disable-shared --with-go=no
CC=afl-clang-fast CXX=afl-clang-fast++ LIBS= ' -lstdc++ '
Я не уверен, почему Libs необходима выше, но я должен был добавить ее, чтобы заставить его компилировать.
Затем построить. Затем «CD -тесты» и запустите «Сделайте test_fuzz_xxx», где xxx является одним из: certauth, mux, ssl, telnet или relpkt. Вам, вероятно, нужно будет настроить некоторые вещи, AFL скажет вам. Обратите внимание, что он будет работать навсегда, вам нужно будет ^C, когда вы закончите.
Makefile в тестах/Makefile.am имеет инструкции о том, как справиться с неспособностью воспроизвести для отладки.
Запуск покрытия кода в библиотеке довольно прост. Сначала вам нужно настроить код, чтобы включить покрытие:
mkdir Ocov ; cd Ocov
../configure --enable-internal-trace=yes
CC= ' gcc -fprofile-arcs -ftest-coverage '
CXX= ' g++ -fprofile-arcs -ftest-coverage '
Компиляция и запустить "Сделай чек".
Чтобы генерировать отчет, запустите:
gcovr -f ' .*/.libs/.* ' -e ' .*python.* '
Это генерирует резюме. Если вы хотите увидеть покрытие отдельных строк в файле, вы можете сделать:
cd lib
gcov -o .libs/ * .o
Вы можете посмотреть в индивидуальных файлах .gcov, созданных для получения информации о том, что покрыто. Смотрите документы GCOV для деталей.
На момент написания статьи я получал около 74% охвата кода, так что это действительно довольно хорошо. Я буду работать над улучшением этого, в основном благодаря улучшению функциональных тестирования.
Ser2net используется для тестирования некоторых вещей, в первую очередь конфигурации последовательного порта (Termios и RFC2217). Вы можете создать Ser2net против версии GCOV библиотеки Gensio и запустить «Make Check» в Ser2net, чтобы получить освещение этих деталей. С этим я вижу около 76% охвата, поэтому он не добавляет многое к общему.
Было бы неплохо иметь возможность комбинировать это с пузырьком, но я не уверен, как это сделать. AFL делает свое дело с покрытием кода. Похоже, есть пакет AFL-COV, который каким-то образом интегрировал GCOV, но я не изучал его.