# How To: Webcam and Microphone Inside of GUI Jail



## BawdyAnarchist (Jan 20, 2021)

One-stop guide on how to get your webcam working inside of a GUI jail, including the microphone. Assumes you already have GUI set up inside the jail. My hardware:  Ryzen Threadripper 3960x, NVIDIA 2080 RTX, Logitec C920 Webcam

*OVERVIEW*
   - Background Info
   - Load Kernel Modules
   - Install / Configure Daemons
   - Start Services
   - Jail Permissions
   - Safe Shutdown
   - Troubleshooting


*BACKGROUND INFO *(not necessary, but helpful for conceptualizing what is occuring)

In FreeBSD you have to configure everything; from bottom to top. In other words, hardware > kernel drivers > daemon configuration > permissions > user-level programs. The process looks something like this:

1. Select compatible hardware. Logitec is typically pretty open, but check the compat:  https://wiki.freebsd.org/WebcamCompat
2. Kernel modules (drivers) interface directly with the hardware_, _and pump raw data feeds to/from daemons.
3. Daemons are an abstraction layer higher. They run in the background, format the datastream, manage access to it, and send control calls to the kernel/hardware. Importantly, they create _device nodes_
4. Device nodes are another abstraction layer. They're the "connection point" (so to speak), for user-level programs to access the device, and can be found in the special directory: _ /dev  _
5. Jails are largely segregated, and denied access to resources like device nodes, unless explicitly given permission by root.
6. Programs: If we configured everything correctly, jailed user programs should be able to access the webcam/mic.


*LOAD KERNEL MODULES*

_cuse_ is the webcam kernel driver, and can be loaded without restarting. To auto load after reboots, the _sysrc_ command adds it to _/etc/rc.conf . _
`kldload cuse`
`sysrc kld_list+=cuse`
_**The sound (snd_*) kernel module should already be enabled by default, but see Troubleshooting at the bottom if not._


*INSTALL / CONFIGURE DAEMONS*

_webcamd _must be run on host. Unless you compile a custom kernel, jails do not have the privilege to create device nodes
_virtual_oss_ also must run on host. Provides fine grain control for selecting mic and speaker input/output device nodes.
`pkg update && pkg install webcamd virtual_oss`

Plug in USB webcam, make sure your speakers are connected/on, and then restart devd
`service devd restart`

Check which _ugenX.Y_ the webcam is attached to. Mine is _ugen2.3_, yours will probably be different.
`usbconfig`

Modify /etc/rc.conf so that webcamd knows which device to use. Otherwise, the _service_ command will fail to start the daemon. Alternatively, you could run the command:  _webcamd -B -d ugen2.3 _ , but using the rc system is better pratice:
`sysrc webcamd_0_flags+="-d ugenX.Y"` _#Substitue your own ugenX.Y

virtual_oss_ config file requires some investigation. We need to know which /dev/dsp device nodes correspond to the microphone and speaker, and then set those in the config file. First, enable verbose output, and check sndstat:
`sysctl hw.snd.verbose=2`
`cat /dev/sndstat`

Look for sections starting with _pcmX: <USB audio> , _and a line below that looks like:  _pcmX:record:dspX.r0  . _The "X" in these lines will be a number, which correspond to the _/dev/dspX _device node for your webcam microphone. A snip of my output:

```
root@dom0:~ # cat /dev/sndstat
...<snip>...
pcm4: <USB audio> at ? kld snd_uaudio (0p:0v/1r:1v)
        snddev flags=0x100002e3<SIMPLEX,AUTOVCHAN,BUSY,MPSAFE,REGISTERED,VPC,PRIO_RD>
        [pcm4:record:dsp4.r0]: spd 32000, fmt 0x00200010, flags 0x00002108, 0x00000005
...<snip>...
```
On my system, the mic is attached to _/dev/dsp4. _Save that for later, it will go in the config file.
Next you need to discover which device node corresponds to your speakers. Unfortunately, I don't know of a faster way than manually trying each node. First, list all possible nodes:

```
root@dom0:~ # ls /dev/dsp*
/dev/dsp0.0
/dev/dsp0.1
/dev/dsp0.3
/dev/dsp1.0
/dev/dsp2.0
/dev/dsp2.1
/dev/dsp3.0
/dev/dsp3.1
/dev/dsp4.0
```
You only need the first number (/dev/dsp0 , /dev/dsp1 ... etc). Send /dev/urandom to each device node, in turn, until you hear static from the speakers [warning, could be LOUD, turn your volume down a bit]:
`cat /dev/urandom >> /dev/dsp0`   # If no static noise, try the next one
`cat /dev/urandom >> /dev/dsp1`   # Continue until you find the right one

Once you have the correct number for /dev/dspX, make sure that sysctl has that number as the default.
`sysctl hw.snd.default_unit=X` #substitute your dsp number for "X"

Now you should know the device nodes for your mic (mine is /dev/dsp4), and speakers (mine is /dev/dsp0). Next, edit the service file for _virtual_oss_. This is a sample of mine. Notice that _-O _and _-R _correspond to the (O)utput and (R)ecording device nodes. This is also documented in virtual_oss() , and in section 3.4 at: https://wiki.freebsd.org/Sound#OSS_.28simple.29
`vi /usr/local/etc/rc.d/virtual_oss`

```
virtual_oss_default_args="\
  -T /dev/sndstat \
  -S \
  -i 1 \
  -C 2 -c 2 \
  -r 48000 \
  -b 24 \
  -s 8.0ms \
  -O /dev/dsp0 \
  -R /dev/dsp4 \
  -d dsp \
  -t dsp.ctl"
```

*START SERVICES*

You should now be ready to start the services. *However, a word of caution. *

If you're operating these devices from a jail, it's likely for security. These daemons make the webcam and mic available to any user/program/jail with permission. *If you leave them running, your mic and webcam could potentially be accessed even when you might not be aware, or intend for them to. *For this reason, I don't recommend enabling these services in /etc/rc.conf, because they will auto start at boot.

Instead, use _service _to start them as needed, and stop when finished.
`service virtual_oss onestart`
`service webcamd onestart`

Check that they started successfully
`service virtual_oss onestatus`
`service webcamd onestatus`


*JAIL PERMISSIONS*

Jails only have acces to what they are allowed, via the file /etc/devfs.rules. At a minimum, you will need to add paths for mixer, dsp, and video. Here's a snip for my guijails, which also includes the nvida device node:

```
# GUI jail specifics
[devfsrules_guijail=6]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add include $devfsrules_jail
add path 'nvidia*' unhide
add path 'mixer*' unhide
add path 'dsp*' unhide
add path 'video*' unhide
```
Now restart devfs for the new rules to be applied.
`service devfs restart`

You should already have this, but just in case, your /etc/jail.conf must specify the _devfs_ruleset_ for each jail (among other things required for a networked GUI jail to function), but just as a minimal example:

```
gui_jailname {
...<snip>...
   mount.devfs;
   devfs_ruleset="6";
}
```
Now start (or restart) the guijail and add the unprivileged user(s) to the webcamd group. You will need to be the root user. There are of course many ways to do this, but just as an example from a host terminal:
`jexec -l -U root <guijail> pw groupmod -n webcamd -m <unprivileged_user>`

And thats it! I tested my setup on Falkon (the FreeBSD developed browser), via the opensource video confrencing site: jitsi.org , which allows you to immediately start video confrences on the fly, with no registration.


*SAFE SHUTDOWN*

As mentioned eariler, best practices are to shutdown the daemon services when not in use. But first, terminate all programs accessing the device node (in other words, stop audio/video/mic playback and recording), or you could be asking for weird bugs. Then you can stop the services:
`service webcamd onestop`
`service virtual_oss onestop`

Unfortunately, virtual_oss changes some obscure setting in sysctl, and doesn't set it back. If you want your speakers to work after stopping the daemons, run the following command:
`sysctl hw.snd.basename_clone=1`
_** Alternatively, you could drop these commands into a shell script, and if you run i3wm, give it a quick-key_


*TROUBLESHOOTING

I can't get any sound output to the speakers*
Check that the mixer vol and pcm are not at zero:  `mixer`
Check that you have a sound module loaded:  `kldstat` and check for a module which starts with "snd_"
Load all possible sound drivers with: `kldload snd_driver` . This is overkill, but it can help ensure it's not a kernel module problem. Then run `service devd restart` , check volumes `mixer` , and re-run the `cat /dev/urandom >> /dev/dspX`

_*Jailed programs don't see the devices*_
From inside the jail: Make sure you can see the correct /dev/dspX AND /dev/videoY device nodes:
`root@guijail:~ # ls /dev`
Check the jail's pw to make sure the user is part of the webcamd group
`root@guijail:~ # pw groupshow -n webcamd`
Make sure the jail doesn't have elevated kern.securelevel. It needs to be "-1" when changing the pw database.
`root@guijail:~ # sysctl kern.securelevel`

_*Everything was working, but now I have problems with mic/camera*_
When in doubt, restart the jail. If that doesn't work, remove the jail, stop the services, restart devd, and start the services again. I found that I might have to start the webcamd before virtual_oss.


----------

