summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Feinerer <feinerer@logic.at>2020-05-09 12:03:20 +0200
committerAaron Marcher <me@drkhsh.at>2020-11-30 21:24:33 +0100
commit9ac721c23fb640de2a6d1f84c84a79b2ccc26691 (patch)
tree39236fa49f4142ff526ca55ba1958e307cf0e6a9
parentaaf279f6ddfb48146fc1a579efd83a55722910b5 (diff)
downloadslstatus-9ac721c23fb640de2a6d1f84c84a79b2ccc26691.tar.gz
slstatus-9ac721c23fb640de2a6d1f84c84a79b2ccc26691.zip
Use the sioctl_open(3) OpenBSD API to access vol
Starting with OpenBSD 6.7 regular users cannot access raw audio devices anymore, for improved security. Instead use the sioctl_open(3) API to access and manipulate audio controls exposed by sndiod(8). On the first call a permanent connection is established with the running sndiod daemon, and call-back functions are registered which are triggered when audio controls are changed (e.g., a USB headset is attached) or when the volume is modified. On subsequent calls we poll for changes; if there are no volume changes this costs virtually nothing. Joint work with Alexandre Ratchov
-rw-r--r--components/volume.c210
-rw-r--r--config.def.h1
-rw-r--r--config.mk1
3 files changed, 161 insertions, 51 deletions
diff --git a/components/volume.c b/components/volume.c
index 61cec90..b6665da 100644
--- a/components/volume.c
+++ b/components/volume.c
@@ -8,69 +8,177 @@
8#include "../util.h" 8#include "../util.h"
9 9
10#if defined(__OpenBSD__) 10#if defined(__OpenBSD__)
11 #include <sys/audioio.h> 11 #include <sys/queue.h>
12 #include <poll.h>
13 #include <sndio.h>
14 #include <stdlib.h>
15
16 struct control {
17 LIST_ENTRY(control) next;
18 unsigned int addr;
19 #define CTRL_NONE 0
20 #define CTRL_LEVEL 1
21 #define CTRL_MUTE 2
22 unsigned int type;
23 unsigned int maxval;
24 unsigned int val;
25 };
26
27 static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls);
28 static struct pollfd *pfds;
29 static struct sioctl_hdl *hdl;
30 static int initialized;
31
32 /*
33 * Call-back to obtain the description of all audio controls.
34 */
35 static void
36 ondesc(void *unused, struct sioctl_desc *desc, int val)
37 {
38 struct control *c, *ctmp;
39 unsigned int type = CTRL_NONE;
40
41 if (desc == NULL)
42 return;
43
44 /* Delete existing audio control with the same address. */
45 LIST_FOREACH_SAFE(c, &controls, next, ctmp) {
46 if (desc->addr == c->addr) {
47 LIST_REMOVE(c, next);
48 free(c);
49 break;
50 }
51 }
52
53 /* Only match output.level and output.mute audio controls. */
54 if (desc->group[0] != 0 ||
55 strcmp(desc->node0.name, "output") != 0)
56 return;
57 if (desc->type == SIOCTL_NUM &&
58 strcmp(desc->func, "level") == 0)
59 type = CTRL_LEVEL;
60 else if (desc->type == SIOCTL_SW &&
61 strcmp(desc->func, "mute") == 0)
62 type = CTRL_MUTE;
63 else
64 return;
65
66 c = malloc(sizeof(struct control));
67 if (c == NULL) {
68 warn("sndio: failed to allocate audio control\n");
69 return;
70 }
71
72 c->addr = desc->addr;
73 c->type = type;
74 c->maxval = desc->maxval;
75 c->val = val;
76 LIST_INSERT_HEAD(&controls, c, next);
77 }
78
79 /*
80 * Call-back invoked whenever an audio control changes.
81 */
82 static void
83 onval(void *unused, unsigned int addr, unsigned int val)
84 {
85 struct control *c;
86
87 LIST_FOREACH(c, &controls, next) {
88 if (c->addr == addr)
89 break;
90 }
91 c->val = val;
92 }
93
94 static void
95 cleanup(void)
96 {
97 struct control *c;
98
99 if (hdl) {
100 sioctl_close(hdl);
101 hdl = NULL;
102 }
103
104 free(pfds);
105 pfds = NULL;
106
107 while (!LIST_EMPTY(&controls)) {
108 c = LIST_FIRST(&controls);
109 LIST_REMOVE(c, next);
110 free(c);
111 }
112 }
113
114 static int
115 init(void)
116 {
117 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
118 if (hdl == NULL) {
119 warn("sndio: cannot open device");
120 goto failed;
121 }
122
123 if (!sioctl_ondesc(hdl, ondesc, NULL)) {
124 warn("sndio: cannot set control description call-back");
125 goto failed;
126 }
127
128 if (!sioctl_onval(hdl, onval, NULL)) {
129 warn("sndio: cannot set control values call-back");
130 goto failed;
131 }
132
133 pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
134 if (pfds == NULL) {
135 warn("sndio: cannot allocate pollfd structures");
136 goto failed;
137 }
138
139 return 1;
140 failed:
141 cleanup();
142 return 0;
143 }
12 144
13 const char * 145 const char *
14 vol_perc(const char *card) 146 vol_perc(const char *unused)
15 { 147 {
16 static int cls = -1; 148 struct control *c;
17 mixer_devinfo_t mdi; 149 int n, v, value;
18 mixer_ctrl_t mc;
19 int afd = -1, m = -1, v = -1;
20 150
21 if ((afd = open(card, O_RDONLY)) < 0) { 151 if (!initialized)
22 warn("open '%s':", card); 152 initialized = init();
153
154 if (hdl == NULL)
23 return NULL; 155 return NULL;
24 }
25 156
26 for (mdi.index = 0; cls == -1; mdi.index++) { 157 n = sioctl_pollfd(hdl, pfds, POLLIN);
27 if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) { 158 if (n > 0) {
28 warn("ioctl 'AUDIO_MIXER_DEVINFO':"); 159 n = poll(pfds, n, 0);
29 close(afd); 160 if (n > 0) {
30 return NULL; 161 if (sioctl_revents(hdl, pfds) & POLLHUP) {
31 } 162 warn("sndio: disconnected");
32 if (mdi.type == AUDIO_MIXER_CLASS && 163 cleanup();
33 !strncmp(mdi.label.name,
34 AudioCoutputs,
35 MAX_AUDIO_DEV_LEN))
36 cls = mdi.index;
37 }
38 for (mdi.index = 0; v == -1 || m == -1; mdi.index++) {
39 if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) {
40 warn("ioctl 'AUDIO_MIXER_DEVINFO':");
41 close(afd);
42 return NULL;
43 }
44 if (mdi.mixer_class == cls &&
45 ((mdi.type == AUDIO_MIXER_VALUE &&
46 !strncmp(mdi.label.name,
47 AudioNmaster,
48 MAX_AUDIO_DEV_LEN)) ||
49 (mdi.type == AUDIO_MIXER_ENUM &&
50 !strncmp(mdi.label.name,
51 AudioNmute,
52 MAX_AUDIO_DEV_LEN)))) {
53 mc.dev = mdi.index, mc.type = mdi.type;
54 if (ioctl(afd, AUDIO_MIXER_READ, &mc) < 0) {
55 warn("ioctl 'AUDIO_MIXER_READ':");
56 close(afd);
57 return NULL; 164 return NULL;
58 } 165 }
59 if (mc.type == AUDIO_MIXER_VALUE)
60 v = mc.un.value.num_channels == 1 ?
61 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] :
62 (mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] >
63 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] ?
64 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] :
65 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
66 else if (mc.type == AUDIO_MIXER_ENUM)
67 m = mc.un.ord;
68 } 166 }
69 } 167 }
70 168
71 close(afd); 169 value = 100;
170 LIST_FOREACH(c, &controls, next) {
171 if (c->type == CTRL_MUTE && c->val == 1)
172 value = 0;
173 else if (c->type == CTRL_LEVEL) {
174 v = (c->val * 100 + c->maxval / 2) / c->maxval;
175 /* For multiple channels return the minimum. */
176 if (v < value)
177 value = v;
178 }
179 }
72 180
73 return bprintf("%d", m ? 0 : v * 100 / 255); 181 return bprintf("%d", value);
74 } 182 }
75#else 183#else
76 #include <sys/soundcard.h> 184 #include <sys/soundcard.h>
diff --git a/config.def.h b/config.def.h
index 0895f6a..93a875a 100644
--- a/config.def.h
+++ b/config.def.h
@@ -59,6 +59,7 @@ static const char unknown_str[] = "n/a";
59 * uptime system uptime NULL 59 * uptime system uptime NULL
60 * username username of current user NULL 60 * username username of current user NULL
61 * vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer) 61 * vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
62 * NULL on OpenBSD
62 * wifi_perc WiFi signal in percent interface name (wlan0) 63 * wifi_perc WiFi signal in percent interface name (wlan0)
63 * wifi_essid WiFi ESSID interface name (wlan0) 64 * wifi_essid WiFi ESSID interface name (wlan0)
64 */ 65 */
diff --git a/config.mk b/config.mk
index 3b32b7c..d88695c 100644
--- a/config.mk
+++ b/config.mk
@@ -14,6 +14,7 @@ X11LIB = /usr/X11R6/lib
14CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE 14CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE
15CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os 15CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os
16LDFLAGS = -L$(X11LIB) -s 16LDFLAGS = -L$(X11LIB) -s
17# OpenBSD: add -lsndio
17LDLIBS = -lX11 18LDLIBS = -lX11
18 19
19# compiler and linker 20# compiler and linker