| 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__ |
|---|
| 21 | void 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 | |
|---|
| 34 | unsigned 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 | |
|---|
| 46 | void 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 */ |
|---|
| 73 | long 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 | */ |
|---|
| 85 | int 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 |
|---|
| 206 | int 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 |
|---|