1 /*-
2 * Copyright (c) 2010-2020 Hans Petter Selasky. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/rtprio.h>
30 #include <sys/time.h>
31 #include <sys/filio.h>
32
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <grp.h>
38 #include <pwd.h>
39 #include <sysexits.h>
40 #include <ctype.h>
41
42 #include <libutil.h>
43
44 #include <cuse.h>
45
46 #include <linux/idr.h>
47
48 static cuse_open_t v4b_open;
49 static cuse_close_t v4b_close;
50 static cuse_read_t v4b_read;
51 static cuse_write_t v4b_write;
52 static cuse_ioctl_t v4b_ioctl;
53 static cuse_poll_t v4b_poll;
54
55 static struct cuse_methods v4b_methods = {
56 .cm_open = v4b_open,
57 .cm_close = v4b_close,
58 .cm_read = v4b_read,
59 .cm_write = v4b_write,
60 .cm_ioctl = v4b_ioctl,
61 .cm_poll = v4b_poll,
62 };
63
64 /*
65 * The first letter in the devicename gives the unit number allocation
66 * group from "A" to "Z". This letter is removed when creating the
67 * character device.
68 */
69 #define UNIT_MAX ('G' - 'A')
70
71 const char *webcamd_devnames[F_V4B_MAX] = {
72
73 [F_V4B_VIDEO] = "Avideo%d",
74
75 [F_V4B_DVB_AUDIO] = "Bdvb/adapter%d/audio%d",
76 [F_V4B_DVB_CA] = "Bdvb/adapter%d/ca%d",
77 [F_V4B_DVB_DEMUX] = "Bdvb/adapter%d/demux%d",
78 [F_V4B_DVB_DVR] = "Bdvb/adapter%d/dvr%d",
79
80 [F_V4B_DVB_FRONTEND] = "Bdvb/adapter%d/frontend%d",
81 [F_V4B_DVB_OSD] = "Bdvb/adapter%d/osd%d",
82 [F_V4B_DVB_SEC] = "Bdvb/adapter%d/sec%d",
83 [F_V4B_DVB_VIDEO] = "Bdvb/adapter%d/video%d",
84
85 [F_V4B_LIRC] = "Clirc%d",
86
87 [F_V4B_EVDEV] = "Dinput/event%d",
88
89 [F_V4B_JOYDEV] = "Einput/js%d",
90
91 [F_V4B_ROCCAT] = "Froccat%d",
92 };
93
94 static int u_unit;
95 static int u_addr;
96 static int u_index;
97 static int u_videodev = -1;
98 static const char *u_devicename;
99 static const char *u_serialname;
100 static int u_match_index;
101 static int do_list;
102 static int do_fork;
103 static int do_realtime = 1;
104 static struct pidfh *local_pid = NULL;
105 static const char *d_desc;
106 static gid_t gid;
107 static uid_t uid;
108 static int gid_found;
109 static int uid_found;
110 static int vtuner_client;
111 static int vtuner_server;
112
113 #define CHR_MODE 0660
114
115 char global_fw_prefix[128] = {"/boot/modules"};
116
117 #define v4b_errx(code, fmt, ...) do { \
118 syslog(LOG_ERR, "webcamd: " fmt "\n",## \
119 __VA_ARGS__); \
120 exit(code); \
121 } while (0)
122
123 static void
v4b_work_exec_hup(int dummy)124 v4b_work_exec_hup(int dummy)
125 {
126
127 }
128
129 static void *
v4b_work(void * arg)130 v4b_work(void *arg)
131 {
132 signal(SIGHUP, v4b_work_exec_hup);
133
134 while (1) {
135 if (cuse_wait_and_process() != 0)
136 break;
137 }
138
139 exit(0); /* we are done */
140
141 return (NULL);
142 }
143
144 static int
v4b_convert_error(int error)145 v4b_convert_error(int error)
146 {
147 ; /* indent fix */
148 if (error < 0) {
149 switch (error) {
150 case -EBUSY:
151 error = CUSE_ERR_BUSY;
152 break;
153 case -EWOULDBLOCK:
154 error = CUSE_ERR_WOULDBLOCK;
155 break;
156 case -EINVAL:
157 case -ENOENT:
158 case -ENOTTY:
159 error = CUSE_ERR_INVALID;
160 break;
161 case -ENOMEM:
162 error = CUSE_ERR_NO_MEMORY;
163 break;
164 case -EFAULT:
165 error = CUSE_ERR_FAULT;
166 break;
167 case -EINTR:
168 error = CUSE_ERR_SIGNAL;
169 break;
170 default:
171 error = CUSE_ERR_OTHER;
172 break;
173 }
174 }
175 return (error);
176 }
177
178 static int
v4b_open(struct cuse_dev * cdev,int fflags)179 v4b_open(struct cuse_dev *cdev, int fflags)
180 {
181 struct cdev_handle *handle;
182 int f_v4b;
183 int fflags_linux;
184
185 f_v4b = (int)(long)cuse_dev_get_priv0(cdev);
186
187 switch (fflags & (CUSE_FFLAG_WRITE | CUSE_FFLAG_READ)) {
188 case (CUSE_FFLAG_WRITE | CUSE_FFLAG_READ):
189 fflags_linux = O_RDWR;
190 break;
191 case CUSE_FFLAG_READ:
192 fflags_linux = O_RDONLY;
193 break;
194 case CUSE_FFLAG_WRITE:
195 fflags_linux = O_WRONLY;
196 break;
197 default:
198 return (CUSE_ERR_INVALID);
199 }
200
201 need_timer(1);
202
203 /* try to open the device */
204 handle = linux_open(f_v4b, fflags_linux);
205
206 if (handle == NULL) {
207 need_timer(0);
208 return (CUSE_ERR_INVALID);
209 }
210 cuse_dev_set_per_file_handle(cdev, handle);
211 return (0);
212 }
213
214 static int
v4b_close(struct cuse_dev * cdev,int fflags)215 v4b_close(struct cuse_dev *cdev, int fflags)
216 {
217 struct cdev_handle *handle;
218 int error;
219
220 handle = cuse_dev_get_per_file_handle(cdev);
221
222 cuse_dev_set_per_file_handle(cdev, NULL);
223
224 /* close device */
225 error = linux_close(handle);
226
227 if (error == 0)
228 need_timer(0);
229
230 return (v4b_convert_error(error));
231 }
232
233 #ifndef CUSE_FFLAG_COMPAT32
234 #define CUSE_FFLAG_COMPAT32 0
235 #endif
236
237 static int
v4b_read(struct cuse_dev * cdev,int fflags,void * peer_ptr,int len)238 v4b_read(struct cuse_dev *cdev, int fflags,
239 void *peer_ptr, int len)
240 {
241 struct cdev_handle *handle;
242 int error;
243
244 handle = cuse_dev_get_per_file_handle(cdev);
245
246 /* read from device */
247 error = linux_read(handle,
248 fflags & (CUSE_FFLAG_NONBLOCK | CUSE_FFLAG_COMPAT32), peer_ptr, len);
249
250 return (v4b_convert_error(error));
251 }
252
253 static int
v4b_write(struct cuse_dev * cdev,int fflags,const void * peer_ptr,int len)254 v4b_write(struct cuse_dev *cdev, int fflags,
255 const void *peer_ptr, int len)
256 {
257 struct cdev_handle *handle;
258 int error;
259
260 handle = cuse_dev_get_per_file_handle(cdev);
261
262 /* write to device */
263 error = linux_write(handle,
264 fflags & (CUSE_FFLAG_NONBLOCK | CUSE_FFLAG_COMPAT32),
265 (uint8_t *)((const uint8_t *)peer_ptr - (const uint8_t *)0), len);
266
267 return (v4b_convert_error(error));
268 }
269
270 unsigned short webcamd_vendor;
271 unsigned short webcamd_product;
272 unsigned int webcamd_speed;
273
274 static int
v4b_ioctl(struct cuse_dev * cdev,int fflags,unsigned long cmd,void * peer_data)275 v4b_ioctl(struct cuse_dev *cdev, int fflags,
276 unsigned long cmd, void *peer_data)
277 {
278 struct v4l2_buffer buf;
279 struct v4l2_buffer_compat32 buf32;
280 struct cdev_handle *handle;
281 void *ptr;
282 int error;
283
284 handle = cuse_dev_get_per_file_handle(cdev);
285
286 /* we support blocking/non-blocking I/O */
287 if (cmd == FIONBIO || cmd == FIOASYNC)
288 return (0);
289
290 /* execute ioctl */
291 error = linux_ioctl(handle,
292 fflags & (CUSE_FFLAG_NONBLOCK | CUSE_FFLAG_COMPAT32),
293 cmd, peer_data);
294
295 if ((cmd == VIDIOC_QUERYBUF) && (error >= 0)) {
296 if (copy_from_user(&buf, peer_data, sizeof(buf)) != 0) {
297 error = -EFAULT;
298 goto done;
299 }
300 ptr = linux_mmap(handle, fflags,
301 (void *)(long)PAGE_SIZE,
302 buf.length, buf.m.offset);
303
304 if (ptr != MAP_FAILED) {
305 buf.m.offset = cuse_vmoffset(ptr);
306 } else {
307 buf.m.offset = 0x80000000UL;
308 }
309
310 if (copy_to_user(peer_data, &buf, sizeof(buf)) != 0) {
311 error = -EFAULT;
312 goto done;
313 }
314 } else if ((cmd == _VIDIOC_QUERYBUF32) && (error >= 0)) {
315 if (copy_from_user(&buf32, peer_data, sizeof(buf32)) != 0) {
316 error = -EFAULT;
317 goto done;
318 }
319 ptr = linux_mmap(handle, fflags,
320 (void *)(long)PAGE_SIZE,
321 buf32.length, buf32.m.offset);
322
323 if (ptr != MAP_FAILED) {
324 buf32.m.offset = cuse_vmoffset(ptr);
325 } else {
326 buf32.m.offset = 0x80000000UL;
327 }
328
329 if (copy_to_user(peer_data, &buf32, sizeof(buf32)) != 0) {
330 error = -EFAULT;
331 goto done;
332 }
333 } else if ((cmd == WEBCAMD_IOCTL_GET_USB_VENDOR_ID) && (error < 0)) {
334 if (copy_to_user(peer_data, &webcamd_vendor,
335 sizeof(webcamd_vendor)) != 0) {
336 error = -EFAULT;
337 goto done;
338 }
339 error = 0;
340 } else if ((cmd == WEBCAMD_IOCTL_GET_USB_PRODUCT_ID) && (error < 0)) {
341 if (copy_to_user(peer_data, &webcamd_product,
342 sizeof(webcamd_product)) != 0) {
343 error = -EFAULT;
344 goto done;
345 }
346 error = 0;
347 } else if ((cmd == WEBCAMD_IOCTL_GET_USB_SPEED) && (error < 0)) {
348 if (copy_to_user(peer_data, &webcamd_speed,
349 sizeof(webcamd_speed)) != 0) {
350 error = -EFAULT;
351 goto done;
352 }
353 error = 0;
354 }
355 done:
356 return (v4b_convert_error(error));
357 }
358
359 static int
v4b_poll(struct cuse_dev * cdev,int fflags,int events)360 v4b_poll(struct cuse_dev *cdev, int fflags, int events)
361 {
362 struct cdev_handle *handle;
363 int error;
364 int revents;
365
366 handle = cuse_dev_get_per_file_handle(cdev);
367
368 /* poll device */
369 error = linux_poll(handle);
370
371 revents = 0;
372
373 if (error & (POLLPRI | POLLIN | POLLRDNORM))
374 revents |= events & CUSE_POLL_READ;
375
376 if (error & (POLLOUT | POLLWRNORM))
377 revents |= events & CUSE_POLL_WRITE;
378 #if 0
379 /* currently we mask away any poll errors */
380 if (error & (POLLHUP | POLLNVAL | POLLERR))
381 revents |= events & CUSE_POLL_ERROR;
382 #endif
383 return (revents);
384 }
385
386 static void
v4b_create(int unit)387 v4b_create(int unit)
388 {
389 struct cdev_handle *handle;
390 pthread_t dummy;
391 unsigned int n;
392 unsigned int p;
393 unsigned int q;
394 int id;
395 char buf[128];
396 int unit_num[UNIT_MAX][F_V4B_SUBDEV_MAX];
397 const char *dname;
398 struct cuse_dev *pdev;
399
400 /* The DVB V2 API is ASYNC and we need to wait for it: */
401 flush_scheduled_work();
402
403 for (n = 0; n != UNIT_MAX; n++) {
404 for (p = 0; p != F_V4B_SUBDEV_MAX; p++) {
405 unit_num[n][p] = (unit < 0) ? -1 : (unit + p);
406 }
407 }
408
409 for (n = 0; n != (F_V4B_MAX * F_V4B_SUBDEV_MAX *
410 F_V4B_SUBSUBDEV_MAX); n++) {
411
412 handle = linux_open(n, O_RDONLY);
413
414 if (handle != NULL) {
415
416 linux_close(handle);
417
418 dname = webcamd_devnames[n / (F_V4B_SUBSUBDEV_MAX *
419 F_V4B_SUBDEV_MAX)];
420 id = dname[0] - 'A';
421 p = (n % F_V4B_SUBDEV_MAX);
422 q = ((n / F_V4B_SUBDEV_MAX) % F_V4B_SUBSUBDEV_MAX);
423
424 if (unit_num[id][p] < 0) {
425 if (cuse_alloc_unit_number_by_id(
426 &unit_num[id][p],
427 CUSE_ID_WEBCAMD(id)) != 0) {
428 v4b_errx(1, "Cannot allocate "
429 "uniq unit number");
430 }
431 }
432 again:
433 pdev = cuse_dev_create(&v4b_methods, (void *)(long)n,
434 0, uid, gid, CHR_MODE, dname + 1,
435 unit_num[id][p], q);
436
437 /*
438 * Resolve device naming conflict with new
439 * kernel evdev module:
440 */
441 if (pdev == NULL && unit < 0 &&
442 (n / (F_V4B_SUBDEV_MAX * F_V4B_SUBSUBDEV_MAX)) == F_V4B_EVDEV &&
443 cuse_alloc_unit_number_by_id(&unit_num[id][p], CUSE_ID_WEBCAMD(id)) == 0)
444 goto again;
445
446 snprintf(buf, sizeof(buf), dname + 1,
447 unit_num[id][p], q);
448
449 syslog(LOG_INFO, "Creating /dev/%s\n", buf);
450
451 for (p = 0; p != 4; p++) {
452 if (pthread_create(&dummy, NULL, v4b_work, NULL)) {
453 v4b_errx(1, "Failed creating Cuse4BSD process");
454 }
455 }
456 }
457 }
458 }
459
460 uid_t
v4b_get_uid(void)461 v4b_get_uid(void)
462 {
463 return (uid);
464 }
465
466 gid_t
v4b_get_gid(void)467 v4b_get_gid(void)
468 {
469 return (gid);
470 }
471
472 int
v4b_get_perm(void)473 v4b_get_perm(void)
474 {
475 return (CHR_MODE);
476 }
477
478 static void
usage(void)479 usage(void)
480 {
481 fprintf(stderr,
482 "usage: webcamd -d [ugen]<unit>.<addr> -i 0 -v -1 -B\n"
483 " -d <USB device>\n"
484 " -i <interface or client number>\n"
485 " -m <parameter>=<value>\n"
486 " -s Show available parameters\n"
487 " -l Show available USB devices\n"
488 " -S <SerialNumberString> as output by -l option\n"
489 " -N <DeviceNameString> as output by -l option\n"
490 " -M <match index> for use with -S and -N options\n"
491 " -v <video device number>\n"
492 " -B Run in background\n"
493 " -f <firmware path> [%s]\n"
494 " -r Do not set realtime priority\n"
495 " -U <user> Set user for character devices\n"
496 " -G <group> Set group for character devices\n"
497 " -D <host:port:ndev> Connect to remote host instead of USB\n"
498 " -L <host:port:ndev> Make DVB device available from TCP/IP\n"
499 " -h Print help\n"
500 "NOTE: The minimum options needed is one of -d, -S, -s, -l, -N or -D\n",
501 global_fw_prefix
502 );
503 exit(EX_USAGE);
504 }
505
506 static const char string_unknown[] = {"unknown"};
507
508 static void
string_filter(char * ptr)509 string_filter(char *ptr)
510 {
511 char *old = ptr;
512 char *tmp = ptr;
513 char ch;
514
515 while ((ch = *ptr) != '\0') {
516 if (isalnum(ch) == 0)
517 *ptr = '-';
518 ptr++;
519 }
520 /* trim minus from tail */
521 while (ptr-- != old) {
522 ch = *ptr;
523 if (ch != '-')
524 break;
525 *ptr = '\0';
526 }
527
528 /* trim minus from head */
529 ptr = old;
530 while ((ch = *ptr) == '-')
531 ptr++;
532 while ((ch = *ptr) != '\0')
533 *old++ = *ptr++;
534 *old = '\0';
535
536 /* check if string is empty */
537 if (*tmp == '\0')
538 strcpy(tmp, string_unknown);
539 }
540
541 struct find_match {
542 struct find_match *next;
543 char *ser;
544 char *txt;
545 uint32_t match_num;
546 };
547
548 static struct find_match *
new_match(struct find_match ** ppfirst,const char * ser,const char * txt)549 new_match(struct find_match **ppfirst, const char *ser, const char *txt)
550 {
551 struct find_match *ptr;
552
553 for (ptr = *ppfirst; ptr != 0; ptr = ptr->next) {
554 if (strcmp(ptr->ser, ser) == 0 &&
555 strcmp(ptr->txt, txt) == 0) {
556 ptr->match_num++;
557 return (ptr);
558 }
559 }
560 ptr = malloc(sizeof(*ptr) + strlen(ser) + strlen(txt) + 2);
561 if (ptr == NULL)
562 v4b_errx(EX_SOFTWARE, "Out of memory");
563 ptr->txt = (char *)(ptr + 1);
564 strcpy(ptr->txt, txt);
565 ptr->ser = ptr->txt + strlen(txt) + 1;
566 strcpy(ptr->ser, ser);
567 ptr->match_num = 0;
568 ptr->next = *ppfirst;
569 *ppfirst = ptr;
570 return (ptr);
571 }
572
573 static void
free_match(struct find_match ** ppfirst)574 free_match(struct find_match **ppfirst)
575 {
576 struct find_match *ptr;
577
578 while (1) {
579 ptr = *ppfirst;
580 if (ptr == NULL)
581 break;
582 *ppfirst = ptr->next;
583 free(ptr);
584 }
585 }
586
587 static void
find_devices(void)588 find_devices(void)
589 {
590 struct LIBUSB20_DEVICE_DESC_DECODED *pddesc;
591 struct libusb20_backend *pbe;
592 struct libusb20_device *pdev;
593 const char *ptr;
594 char ser[128];
595 char txt[128];
596 char *sub;
597 int found = 0;
598 int match_number = 0;
599 struct find_match *first_match = NULL;
600 struct find_match *curr_match = NULL;
601
602 pbe = libusb20_be_alloc_default();
603 if (pbe == NULL)
604 v4b_errx(EX_SOFTWARE, "Cannot allocate USB backend");
605
606 if (do_list != 0)
607 printf("Available device(s):\n");
608
609 pdev = NULL;
610 while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
611 if (libusb20_dev_get_mode(pdev) != LIBUSB20_MODE_HOST)
612 continue;
613
614 ptr = libusb20_dev_get_desc(pdev);
615 if (ptr != NULL) {
616 sub = strchr(ptr, '<');
617 if (sub == NULL)
618 strcpy(txt, string_unknown);
619 else
620 strlcpy(txt, sub + 1, sizeof(txt));
621 sub = strchr(txt, '>');
622 if (sub != NULL)
623 *sub = 0;
624 } else {
625 strcpy(txt, string_unknown);
626 }
627
628 pddesc = libusb20_dev_get_device_desc(pdev);
629 if (pddesc == NULL || pddesc->iSerialNumber == 0 ||
630 libusb20_dev_open(pdev, 0) != 0 ||
631 libusb20_dev_req_string_simple_sync(pdev,
632 pddesc->iSerialNumber, ser, sizeof(ser)) != 0) {
633 strcpy(ser, string_unknown);
634 }
635 libusb20_dev_close(pdev);
636
637 string_filter(ser);
638 string_filter(txt);
639
640 if (do_list) {
641 curr_match = new_match(&first_match, ser, txt);
642
643 printf("webcamd [-d ugen%u.%u] -N %s -S %s -M %d\n",
644 libusb20_dev_get_bus_number(pdev),
645 libusb20_dev_get_address(pdev),
646 txt, ser, curr_match->match_num);
647
648 } else if ((u_devicename == NULL || strcmp(txt, u_devicename) == 0) &&
649 (u_serialname == NULL || strcmp(ser, u_serialname) == 0) &&
650 (u_addr == 0 || (libusb20_dev_get_address(pdev) == u_addr &&
651 libusb20_dev_get_bus_number(pdev) == u_unit))) {
652
653 if (found++ == u_match_index) {
654 u_unit = libusb20_dev_get_bus_number(pdev);
655 u_addr = libusb20_dev_get_address(pdev);
656 libusb20_be_free(pbe);
657 free_match(&first_match);
658 return;
659 }
660 }
661 }
662 libusb20_be_free(pbe);
663 free_match(&first_match);
664
665 if (do_list != 0) {
666 printf("Show webcamd usage:\n"
667 "webcamd -h\n");
668 if (curr_match != NULL)
669 exit(0);
670 }
671 v4b_errx(EX_SOFTWARE, "No USB device match found");
672 }
673
674 static void
v4b_exit(void)675 v4b_exit(void)
676 {
677 if (local_pid != NULL) {
678 pidfile_remove(local_pid);
679 local_pid = NULL;
680 }
681 closelog();
682 }
683
684 int
pidfile_create(int bus,int addr,int index)685 pidfile_create(int bus, int addr, int index)
686 {
687 char buf[64];
688
689 if (local_pid != NULL)
690 return (0);
691
692 snprintf(buf, sizeof(buf), "/var/run/webcamd."
693 "%d.%d.%d.pid", bus, addr, index);
694
695 local_pid = pidfile_open(buf, 0600, NULL);
696 if (local_pid == NULL) {
697 return (EEXIST);
698 } else {
699 pidfile_write(local_pid);
700 }
701
702 syslog(LOG_INFO, "Attached to ugen%d.%d[%d]\n", bus, addr, index);
703
704 return (0);
705 }
706
707 /*
708 * The following three functions were copied from FreeBSD's chown
709 * utility.
710 */
711 static uid_t
id(const char * name,const char * type)712 id(const char *name, const char *type)
713 {
714 uid_t val;
715 char *ep;
716
717 val = strtoul(name, &ep, 10);
718 if (*ep != '\0')
719 v4b_errx(EX_USAGE, "%s: illegal %s name", name, type);
720 return (val);
721 }
722
723 static void
a_gid(const char * s)724 a_gid(const char *s)
725 {
726 struct group *gr;
727
728 gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
729 gid_found = 1;
730 }
731
732 static void
a_uid(const char * s)733 a_uid(const char *s)
734 {
735 struct passwd *pw;
736
737 uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
738 uid_found = 1;
739 }
740
741 int
main(int argc,char ** argv)742 main(int argc, char **argv)
743 {
744 const char *params = "N:Bd:f:i:M:m:S:sv:hHrU:G:D:lL:";
745 char *ptr;
746 int opt;
747 int opt_valid = 0;
748
749 openlog("webcamd", LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_DAEMON);
750 while ((opt = getopt(argc, argv, params)) != -1) {
751 switch (opt) {
752 case 'd':
753 ptr = optarg;
754
755 if ((ptr[0] == 'u') &&
756 (ptr[1] == 'g') &&
757 (ptr[2] == 'e') &&
758 (ptr[3] == 'n'))
759 ptr += 4;
760
761 if (sscanf(ptr, "%d.%d", &u_unit, &u_addr) != 2)
762 usage();
763 break;
764
765 case 'M':
766 u_match_index = atoi(optarg);
767 break;
768
769 case 'N':
770 u_devicename = optarg;
771 break;
772
773 case 'S':
774 u_serialname = optarg;
775 break;
776
777 case 'l':
778 do_list = 1;
779 break;
780
781 case 'i':
782 u_index = atoi(optarg);
783 break;
784
785 case 'D':
786 case 'L':
787 case 's':
788 opt_valid = 1;
789 break;
790 case 'm':
791 break;
792
793 case 'v':
794 u_videodev = atoi(optarg);
795 break;
796
797 case 'B':
798 do_fork = 1;
799 break;
800
801 case 'f':
802 strlcpy(global_fw_prefix, optarg,
803 sizeof(global_fw_prefix));
804 break;
805
806 case 'r':
807 do_realtime = 0;
808 break;
809
810 case 'H':
811 break;
812
813 case 'U':
814 a_uid(optarg);
815 break;
816
817 case 'G':
818 a_gid(optarg);
819 break;
820
821 default:
822 usage();
823 break;
824 }
825 }
826
827 if (!uid_found)
828 a_uid("webcamd");
829
830 if (!gid_found)
831 a_gid("webcamd");
832
833 if (u_devicename != NULL || u_serialname != NULL || do_list != 0) {
834 find_devices();
835 } else if (u_addr == 0 && opt_valid == 0) {
836 /* list devices by default if no option was specified */
837 do_list = 1;
838 find_devices();
839 }
840 if (do_fork) {
841 /* need to daemonise before creating any threads */
842 if (pidfile_create(u_unit, u_addr, u_index)) {
843 syslog(LOG_ERR, "Webcamd is already running for "
844 "ugen%d.%d.%d\n", u_unit, u_addr, u_index);
845 exit(EX_USAGE);
846 }
847 if (daemon(0, 0) != 0)
848 v4b_errx(EX_USAGE, "Cannot daemonize");
849 }
850 atexit(&v4b_exit);
851
852 if (cuse_init() != 0) {
853 v4b_errx(EX_USAGE, "Could not open /dev/cuse. "
854 "Did you kldload cuse4bsd?");
855 }
856 if (do_realtime != 0) {
857 struct rtprio rtp;
858
859 memset(&rtp, 0, sizeof(rtp));
860
861 rtp.type = RTP_PRIO_REALTIME;
862 rtp.prio = 8;
863
864 if (rtprio(RTP_SET, getpid(), &rtp) != 0)
865 syslog(LOG_WARNING, "Cannot set realtime priority\n");
866 }
867 /* get all module parameters registered */
868
869 linux_parm();
870
871 /* process all module parameters */
872
873 optreset = 1;
874 optind = 1;
875
876 while ((opt = getopt(argc, argv, params)) != -1) {
877 switch (opt) {
878 char *ndev;
879 char *type;
880 char *host;
881 char *port;
882
883 case 'm':
884 ptr = strstr(optarg, "=");
885 if (ptr == NULL) {
886 v4b_errx(EX_USAGE, "invalid parameter for "
887 "-m option: '%s'", optarg);
888 }
889 *ptr = 0;
890 if (mod_set_param(optarg, ptr + 1) < 0) {
891 syslog(LOG_WARNING, "cannot set module "
892 "parameter '%s'='%s'\n", optarg, ptr + 1);
893 }
894 break;
895
896 case 'D':
897 host = optarg;
898 port = strstr(host, ":");
899 if (port == NULL) {
900 v4b_errx(EX_USAGE, "invalid syntax for "
901 "-D option: '%s'", optarg);
902 }
903 *port++ = 0;
904 ndev = strstr(port, ":");
905 if (ndev == NULL) {
906 v4b_errx(EX_USAGE, "invalid syntax for "
907 "-D option: '%s'", port);
908 }
909 *ndev++ = 0;
910 ptr = strstr(ndev, ":");
911 if (ptr != NULL)
912 *ptr = 0;
913
914 if (mod_set_param("vtuner_client.devices", ndev) < 0 ||
915 mod_set_param("vtuner_client.host", host) < 0 ||
916 mod_set_param("vtuner_client.port", port) < 0) {
917 v4b_errx(EX_USAGE, "Cannot set all module "
918 "parameters for vTuner client.");
919 }
920 break;
921
922 case 'L':
923 host = optarg;
924 port = strstr(host, ":");
925 if (port == NULL) {
926 v4b_errx(EX_USAGE, "invalid syntax for "
927 "-L option: '%s'", optarg);
928 }
929 *port++ = 0;
930 ndev = strstr(port, ":");
931 if (ndev == NULL) {
932 v4b_errx(EX_USAGE, "invalid syntax for "
933 "-L option: '%s'", port);
934 }
935 *ndev++ = 0;
936 ptr = strstr(ndev, ":");
937 if (ptr != NULL)
938 *ptr = 0;
939
940 if (mod_set_param("vtuner_server.devices", ndev) < 0 ||
941 mod_set_param("vtuner_server.host", host) < 0 ||
942 mod_set_param("vtuner_server.port", port) < 0) {
943 v4b_errx(EX_USAGE, "Cannot set all module "
944 "parameters for vTuner server.");
945 }
946 break;
947
948 case 's':
949 printf("List of available parameters:\n");
950 mod_show_params();
951 exit(0);
952
953 default:
954 break;
955 }
956 }
957 if (mod_get_int_param("vtuner_client.devices") != 0)
958 vtuner_client = 1;
959
960 if (mod_get_int_param("vtuner_server.devices") != 0)
961 vtuner_server = 1;
962
963 if (vtuner_client && vtuner_server)
964 v4b_errx(EX_USAGE, "Cannot specify both vTuner server and client");
965
966 /* system init */
967
968 thread_init();
969 idr_init_cache();
970
971 /* run rest of Linux init code */
972
973 linux_init();
974 linux_late();
975
976 if (vtuner_client == 0) {
977 if (usb_linux_probe_p(&u_unit, &u_addr, &u_index, &d_desc) < 0)
978 v4b_errx(EX_USAGE, "Cannot find USB device");
979 }
980 if (vtuner_server == 0) {
981 v4b_create(u_videodev);
982
983 v4b_work(NULL);
984 } else {
985 while (1)
986 pause();
987 }
988 return (0);
989 }
990
991 unsigned long
copy_to_user(void * to,const void * from,unsigned long n)992 copy_to_user(void *to, const void *from, unsigned long n)
993 {
994 int error;
995
996 if (vtuner_server != 0) {
997 memcpy(to, from, n);
998 return (0);
999 }
1000 #ifdef CONFIG_COMPAT
1001 error = compat_copy_to_user(to, from, n);
1002 if (error != 0)
1003 #endif
1004 error = cuse_copy_out(from, to, (int)n);
1005
1006 return ((error != 0) ? n : 0);
1007 }
1008
1009 unsigned long
copy_from_user(void * to,const void * from,unsigned long n)1010 copy_from_user(void *to, const void *from, unsigned long n)
1011 {
1012 int error;
1013
1014 if (vtuner_server != 0) {
1015 memcpy(to, from, n);
1016 return (0);
1017 }
1018 #ifdef CONFIG_COMPAT
1019 error = compat_copy_from_user(to, from, n);
1020 if (error != 0)
1021 #endif
1022 error = cuse_copy_in(from, to, (int)n);
1023
1024 return ((error != 0) ? n : 0);
1025 }
1026
1027 unsigned long
copy_in_user(void * to,const void * from,unsigned long len)1028 copy_in_user(void *to, const void *from, unsigned long len)
1029 {
1030 uint8_t buffer[PAGE_SIZE] __aligned(sizeof(void *));
1031 unsigned long oldlen = len;
1032
1033 while (len > 0) {
1034 unsigned long delta = len;
1035 if (delta > sizeof(buffer))
1036 delta = sizeof(buffer);
1037
1038 if (copy_from_user(buffer, from, delta) != 0)
1039 return (oldlen);
1040 if (copy_to_user(to, buffer, delta) != 0)
1041 return (oldlen);
1042
1043 to = (char *)to + delta;
1044 from = (const char *)from + delta;
1045 len -= delta;
1046 }
1047 return (0); /* success */
1048 }
1049
1050 static uint32_t zero_alloc[1];
1051
1052 int
is_vmalloc_addr(void * addr)1053 is_vmalloc_addr(void *addr)
1054 {
1055 return (cuse_is_vmalloc_addr(addr));
1056 }
1057
1058 void *
malloc_vm(size_t size)1059 malloc_vm(size_t size)
1060 {
1061 if (size == 0)
1062 return (zero_alloc);
1063
1064 return (cuse_vmalloc(size));
1065 }
1066
1067 void
free_vm(void * ptr)1068 free_vm(void *ptr)
1069 {
1070 if (ptr == zero_alloc)
1071 return;
1072
1073 cuse_vmfree(ptr);
1074 }
1075
1076 int
check_signal(void)1077 check_signal(void)
1078 {
1079 return (cuse_got_peer_signal() == 0 ||
1080 thread_got_stopping() == 0);
1081 }
1082
1083 void
poll_wakeup_internal(void)1084 poll_wakeup_internal(void)
1085 {
1086 cuse_poll_wakeup();
1087 }
1088
1089 struct cdev_handle *
get_current_cdev_handle(void)1090 get_current_cdev_handle(void)
1091 {
1092 struct cuse_dev *cdev;
1093
1094 cdev = cuse_dev_get_current(NULL);
1095
1096 if (cdev == NULL)
1097 return (NULL);
1098
1099 return (cuse_dev_get_per_file_handle(cdev));
1100 }
1101