How To: Webcam and Microphone Inside of GUI Jail

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:
Code:
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:
Code:
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
Code:
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:
Code:
# 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:
Code:
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.
 
Back
Top