source: trunk/src/ifd/utils.c @ 1114

Revision 1114, 4.9 KB checked in by alonbl, 3 years ago (diff)

Support event mode for drivers

Current implementation forces poll loop every one second or so,
this consume CPU and power.

This changeset adds the ability for a driver to provide file
descriptor for event handling, removing the need to poll.

Only Linux USB interface was modified to use this new interface.

All other environments will fall-back into the poll mode.

New driver methods:

before_command

Executed before command, makes it easy to remove event capture.

after_command

Execute after command, makes it easy to add event capture.

get_eventfd

Get an event fd out of the driver, if unsupported -1 should
be returned, this fall-back into the poll mode.

event

Event method to call with th evntfd is signaled.

before_command

Execute whatever needed before executing a command. Useful
if driver need to turn off event capture.

after_command

Execute whatever needed before blocking. Useful if driver
need to turn on event capture.

New device methods:

get_eventfd

Gets eventfd if available.

New USB methods:

ifd_usb_get_eventfd

Get event fd to poll for events.

ifd_usb_capture_event

None blocking capture.

New CT socket methods:

error

Called with POLLERR is received.

New configuration file options:

force_poll

Force polling, default is true (as correct behavior),
until Linux kernel will resolve its issues.

New command-line options:

ifdhandler -p

Force polling.

Linux specific notes:

This enable safe detection of device removal and allow USB drivers
to stop polling device, even if they do not have interrupt interface.

As OpenCT uses /dev/bus/usb namespace if exists, and this feature worked
only on usbfs prior to linux-2.6.28, it will not work with older kernels.

linux-2.6.28 has a bug that cause the signal not to be sent if kernel
is configured without CONFIG_USB_DEVICEFS, see [1].

linux-2.6.29 is expected to solve this issue.

Even if ccid driver works correctly, as it uses interrupt endpoint,
by default polling will be forced.

[1]  http://marc.info/?t=123075926900001&r=1&w=2

M src/ifd/ifdhandler.c
M src/ifd/utils.c
M src/ifd/process.c
M src/ifd/sys-osx.c
M src/ifd/sys-null.c
M src/ifd/device.c
M src/ifd/internal.h
M src/ifd/usb.c
M src/ifd/reader.c
M src/ifd/sys-linux.c
M src/ifd/sys-bsd.c
M src/ifd/sys-solaris.c
M src/ifd/sys-sunray.c
M src/include/openct/driver.h
M src/include/openct/device.h
M src/include/openct/ifd.h
M src/include/openct/socket.h
M src/ct/mainloop.c
M etc/openct.conf.in

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * Utility functions
3 *
4 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
5 */
6
7#include "internal.h"
8#include <stdio.h>
9#include <stdlib.h>
10#include <stdarg.h>
11#include <fcntl.h>
12#include <string.h>
13#include <unistd.h>
14#include <sys/stat.h>
15#include <sys/wait.h>
16#include <sys/types.h>
17#include <pwd.h>
18#include <grp.h>
19
20#ifndef __GNUC__
21void ifd_debug(int level, const char *fmt, ...)
22{
23        va_list ap;
24        char str[2048];
25
26        va_start(ap, fmt);
27        vsnprintf(str, sizeof(str), fmt, ap);
28        if (level <= ct_config.debug)
29                ct_debug(str);
30        va_end(ap);
31}
32#endif
33
34unsigned int ifd_count_bits(unsigned int word)
35{
36        static unsigned int bcount[16] = {
37                0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
38        };
39        unsigned int num;
40
41        for (num = 0; word; word >>= 4)
42                num += bcount[word & 0xF];
43        return num;
44}
45
46void ifd_revert_bits(unsigned char *data, size_t len)
47{
48        unsigned char j, k, c, d;
49
50        while (len--) {
51                c = *data;
52                for (j = 1, k = 0x80, d = 0; k != 0; j <<= 1, k >>= 1) {
53                        if (c & j)
54                                d |= k;
55                }
56                *data++ = d ^ 0xFF;
57        }
58}
59
60#ifndef timersub
61# define timersub(a, b, result)                                               \
62  do {                                                                        \
63    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
64    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
65    if ((result)->tv_usec < 0) {                                              \
66      --(result)->tv_sec;                                                     \
67      (result)->tv_usec += 1000000;                                           \
68    }                                                                         \
69  } while (0)
70#endif
71
72/* return time elapsed since "then" in miliseconds */
73long ifd_time_elapsed(struct timeval *then)
74{
75        struct timeval now, delta;
76
77        gettimeofday(&now, NULL);
78        timersub(&now, then, &delta);
79        return delta.tv_sec * 1000 + (delta.tv_usec / 1000);
80}
81
82/*
83 * Spawn an ifdhandler
84 */
85int ifd_spawn_handler(const char *driver, const char *devtype, int idx)
86{
87        const char *argv[16];
88        char reader[16], debug[10];
89        char *type, *device;
90        int argc, n;
91        pid_t pid;
92        char *user = NULL;
93        int force_poll = 1;
94
95        ifd_debug(1, "driver=%s, devtype=%s, index=%d", driver, devtype, idx);
96
97        if ((pid = fork()) < 0) {
98                ct_error("fork failed: %m");
99                return 0;
100        }
101
102        if (pid != 0) {
103                /* We're the parent process. The child process should
104                 * call daemon(), causing the process to exit
105                 * immediately after allocating a slot in the status
106                 * file. We wait for it here to make sure USB devices
107                 * don't claim a slot reserved for another device */
108                waitpid(pid, NULL, 0);
109                return 1;
110        }
111
112        argc = 0;
113        argv[argc++] = ct_config.ifdhandler;
114
115        if (idx >= 0) {
116                snprintf(reader, sizeof(reader), "-r%u", idx);
117                argv[argc++] = reader;
118        } else {
119                argv[argc++] = "-H";
120        }
121
122        if (ct_config.debug) {
123                if ((n = ct_config.debug) > 6)
124                        n = 6;
125                debug[n + 1] = '\0';
126                while (n--)
127                        debug[n + 1] = 'd';
128                debug[0] = '-';
129                argv[argc++] = debug;
130        }
131
132        ifd_conf_get_bool("ifdhandler.force_poll", &force_poll);
133        if (force_poll) {
134                argv[argc++] = "-p";
135        }
136
137        type = strdup(devtype);
138        device = strtok(type, ":");
139        device = strtok(NULL, ":");
140        if (!device || !type) {
141                ct_error("failed to parse devtype %s", devtype);
142                exit(1);
143        }
144
145        argv[argc++] = driver;
146        argv[argc++] = type;
147        argv[argc++] = device;
148        argv[argc] = NULL;
149
150        n = getdtablesize();
151        while (--n > 2)
152                close(n);
153       
154        if ((n = ifd_conf_get_string_list("ifdhandler.groups", NULL, 0)) > 0) {
155                char **groups = (char **)calloc(n, sizeof(char *));
156                gid_t *gids = (gid_t *)calloc(n, sizeof(gid_t));
157                int j;
158                if (!groups || !gids) {
159                        ct_error("out of memory");
160                        exit(1);
161                }
162                n = ifd_conf_get_string_list("ifdhandler.groups", groups, n);
163                for (j = 0; j < n; j++) {
164                        struct group *g = getgrnam(groups[j]);
165                        if (g == NULL) {
166                                ct_error("failed to parse group %s", groups[j]);
167                                exit(1);
168                        }
169                        gids[j] = g->gr_gid;
170                }
171                if (setgroups(n-1, &gids[1]) == -1) {
172                        ct_error("failed set groups %m");
173                        exit(1);
174                }
175                if (setgid(gids[0]) == -1) {
176                        ct_error("failed setgid %d %m", gids[0]);
177                        exit(1);
178                }
179                free(groups);
180                free(gids);
181        }
182
183        if (ifd_conf_get_string("ifdhandler.user", &user) >= 0) {
184                struct passwd *p = getpwnam(user);
185
186                if (p == NULL) {
187                        ct_error("failed to parse user %s", user);
188                        exit(1);
189                }
190
191                if (setuid(p->pw_uid) == -1) {
192                        ct_error("failed to set*uid user %s %m", user);
193                        exit(1);
194                }
195        }
196
197        execv(ct_config.ifdhandler, (char **)argv);
198        ct_error("failed to execute %s: %m", ct_config.ifdhandler);
199        exit(1);
200}
201
202/*
203 * Replacement for the BSD daemon() function
204 */
205#ifndef HAVE_DAEMON
206int daemon(int nochdir, int noclose)
207{
208        pid_t pid;
209
210        pid = fork();
211
212        /* In case of fork is error. */
213        if (pid < 0)
214                return -1;
215
216        /* In case of this is parent process. */
217        if (pid != 0)
218                exit(0);
219
220        /* Become session leader and get pid. */
221        pid = setsid();
222
223        if (pid < -1) {
224                perror("setsid");
225                return -1;
226        }
227
228        /* Change directory to root. */
229        if (!nochdir)
230                chdir("/");
231
232        /* File descriptor close. */
233        if (!noclose) {
234                int fd;
235
236                fd = open("/dev/null", O_RDWR, 0);
237                if (fd != -1) {
238                        dup2(fd, STDIN_FILENO);
239                        dup2(fd, STDOUT_FILENO);
240                        dup2(fd, STDERR_FILENO);
241                        if (fd > 2)
242                                close(fd);
243                }
244        }
245        umask(0027);
246        return 0;
247}
248#endif
Note: See TracBrowser for help on using the repository browser.