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