I'm writing a simple application in C, running on a Raspberry Pi, that makes use of the D2XX drivers to communicate with a serial port device. I've followed a number of online tutorials and reference guides to get it working, and have taken steps such as setting up custom udev rules to ensure the drivers can load properly, I followed FTDI's build instructions to install the shared library, I use the -l
argument of gcc to link in the library when compiling, and I run my C program with sudo
to ensure the drivers have proper access. And that has been a success! The program works as intended.
Now I am trying to convert my simple program into a daemon process that can be controlled with an init.d script (a la service start
), and have run into trouble.
For simplicity's sake, here is a watered down version of my C program which does work:
myprog.c:
#include <stdlib.h>
#include "ftd2xx.h"
int main(int argc, char *argv[])
{
DWORD i, iNumDevs = 0;
char *serialNumber = malloc(64);
FT_STATUS ftStatus = FT_CreateDeviceInfoList(&iNumDevs);
for (i = 0; i < iNumDevs; i++) {
ftStatus = FT_ListDevices((PVOID)i, serialNumber, FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER);
if (FT_OK == ftStatus) {
break;
}
}
// more code here...
return EXIT_SUCCESS;
}
I compile that with gcc -lftd2xx -o myprog myprog.c
and then run it with sudo ./myprog
, and take my word for it that it does everything that it's supposed to do. But now that I am trying to re-work this same code into a daemon, I've been following some other online tutorials, and the code above has been transformed into something that looks more like this. Currently, this does not work:
mydaemon.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "ftd2xx.h"
int main(int argc, char *argv[])
{
pid_t pid, sid;
pid = fork();
if (pid < 0) {
return EXIT_FAILURE;
}
if (pid > 0) {
return EXIT_SUCCESS;
}
umask(0);
openlog("mydaemon", LOG_PID|LOG_CONS, LOG_USER);
sid = setsid();
if (sid < 0) {
syslog(LOG_ERR, "Failed to set session ID on child process");
return EXIT_FAILURE;
}
if ((chdir("/")) < 0) {
syslog(LOG_ERR, "Failed to change working directory");
return EXIT_FAILURE;
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while (1) {
DWORD i, iNumDevs = 0;
char *serialNumber = malloc(64);
syslog(LOG_INFO, "I get to this line");
FT_STATUS ftStatus = FT_CreateDeviceInfoList(&iNumDevs);
syslog(LOG_INFO, "I do not get to this line :( ");
// more code here...
sleep(10);
}
return EXIT_SUCCESS;
}
I compile that program in the exact same way: gcc -lftd2xx -o mydaemon mydaemon.c
; I run it the same way: sudo ./mydaemon
, but unfortunately it does not work. In a separate console window I'm tailing the /var/log/messages
file, and I can clearly see it reach my first log message (i.e. "I can get to this line"), but immediately after that it is dead in the water. I never see the second log message, and indeed, at that point the program becomes totally unresponsive. I have to find its process ID and kill it.
In other words, as soon as it tries to make a call to the D2XX drivers in the forked process, it fails. What am I doing wrong? I've already demonstrated with the first example that the code does work, so what is it about running as a daemon that causes it to completely break down? As far as I can tell it doesn't even get a chance to execute the D2XX method in question; it's as if it simply can't find the method in the first place, while running in the forked process.
Probably because it uses libusb
... and they appear to do it badly.
See my question here: libusb-1.0 hotplug events stop working in parent after fork(), when child calls libusb_exit()
And the discussion here: https://github.com/libusb/libusb/issues/268
My specific problem was related to hotplug events, but I expect that other things will go wrong for you too.
The reason that this is less obvious in your scenario is because they are probably doing some setup / init when the library loads (how kind of them), rather than when you start to use it.
As pointed out by @duskwuff, there is another answer here: https://stackoverflow.com/a/35186414/149341
I've just done some playing, follow along below:
cd $(mktemp -d)
curl http://www.ftdichip.com/Drivers/D2XX/Linux/libftd2xx-x86_64-1.3.6.tgz | tar -xvz
Put this in test.c
:
#include <stdio.h>
#include "ftd2xx.h"
int main(void) {
int num_devs;
fprintf(stderr, "in to main()\n");
FT_STATUS ft_status = FT_CreateDeviceInfoList(&num_devs);
fprintf(stderr, "FT_CreateDeviceInfoList() returned: %d\n", ft_status);
fprintf(stderr, "out of main()\n");
return 0;
}
Compile:
gcc test.c -o test -g -I release -L release/build -lftd2xx -ldl -lpthread
Now in gdb
:
[...]
Reading symbols from test...done.
(gdb) b libusb_init
Breakpoint 1 at 0x40cd20
(gdb) start
Temporary breakpoint 2 at 0x401d75: file test.c, line 7.
Starting program: /tmp/tmp.jJpBNywVzB/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, 0x000000000040cd20 in libusb_init ()
(gdb) bt
#0 0x000000000040cd20 in libusb_init ()
#1 0x0000000000401f36 in my_init ()
#2 0x000000000041a05d in __libc_csu_init ()
#3 0x00007ffff7614ed5 in __libc_start_main (main=0x401d6d <main>, argc=1, argv=0x7fffffffe228, init=0x41a010 <__libc_csu_init>, fini=<optimised out>, rtld_fini=<optimised out>, stack_end=0x7fffffffe218)
at libc-start.c:246
#4 0x0000000000401ca9 in _start ()
(gdb)
It hit the breakpoint on libusb_init()
before even getting to main()
, called from their my_init()
.