Cleaned up most of the prototypes + static qualifiers.
[playerz] / notify.c
1 /* notify.c: Service IPC via POSIX  message queues.
2
3    Copyright (C) 2019 Michael Zucchi
4
5    This program is free software: you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation, either version 3 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program. If not, see
17    <http://www.gnu.org/licenses/>.
18 */
19
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <mqueue.h>
23
24 #include <poll.h>
25
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <errno.h>
29
30 #include "dbindex.h"
31
32 #include "notify.h"
33
34 // Message handling and routing
35
36 static ez_blob_desc PLAY_SEEK_DESC[] = {
37         EZ_BLOB_START(struct notify_play_seek),
38         EZ_BLOB_INT32(struct notify_play_seek, 1, mode),
39         EZ_BLOB_FLOAT64(struct notify_play_seek, 2, stamp),
40         EZ_BLOB_END(struct notify_play_seek)
41 };
42
43 static ez_blob_desc DEBUG_DESC[] = {
44         EZ_BLOB_START(struct notify_debug),
45         EZ_BLOB_INT32(struct notify_debug, 1, func),
46         EZ_BLOB_END(struct notify_debug)
47 };
48
49 static ez_blob_desc KEY_DESC[] = {
50         EZ_BLOB_START(struct notify_key),
51         EZ_BLOB_INT32(struct notify_key, 1, code),
52         EZ_BLOB_END(struct notify_key)
53 };
54
55 /**
56  * Blob for action.
57  * This must match enum notify_action.
58  */
59 static ez_blob_desc *action_desc[] = {
60         NULL,
61
62         DBDISK_DESC,
63         DBDISK_DESC,
64
65         NULL,
66         NULL,
67         NULL,
68         PLAY_SEEK_DESC,
69
70         NULL,
71         NULL,
72
73         NULL,
74         NULL,
75         NULL,
76
77         NULL,
78
79         KEY_DESC,
80
81         DEBUG_DESC,
82
83         NULL
84 };
85
86 // should be global by default
87 // could force a smaller one i guess
88 static long msg_size;
89
90 /**
91  * Create a reader queue, these are blocking.
92  */
93 mqd_t notify_reader_new(const char *path) {
94         mqd_t q = mq_open(path, O_RDONLY | O_CREAT, 0600, NULL);
95
96         if (q != -1) {
97                 struct mq_attr at;
98                 mq_getattr(q, &at);
99                 msg_size = at.mq_msgsize;
100         }
101
102         return q;
103 }
104
105 /**
106  * Create a writer queue, these are non-blocking.
107  */
108 mqd_t notify_writer_new(const char *path) {
109         mqd_t q = mq_open(path, O_WRONLY | O_CREAT | O_NONBLOCK, 0600, NULL);
110
111         if (q != -1) {
112                 struct mq_attr at;
113                 mq_getattr(q, &at);
114                 msg_size = at.mq_msgsize;
115         }
116
117         return q;
118 }
119
120 void notify_close(mqd_t q) {
121         mq_close(q);
122 }
123
124 void notify_msg_free(enum notify_action action, void *p) {
125         if (action_desc[action]) {
126                 ez_blob_free(action_desc[action], p);
127         }
128 }
129
130 /**
131  * Return the number of messages waiting.
132  */
133 int notify_msg_ready(notify_t q) {
134         struct mq_attr at;
135
136         mq_getattr(q, &at);
137         return (int)at.mq_curmsgs;
138 }
139
140 /**
141  * Send a message.  This does not block if the queue is full.
142  *
143  * @return -1 on error, EGAIN means the queue is full.
144  */
145 int notify_msg_send(mqd_t q, enum notify_action action, unsigned int msg_pri, const void *p) {
146         int res = -1;
147
148         if (action < NOTIFY_SIZEOF) {
149                 printf("send action %d\n", action);
150                 if (action_desc[action]) {
151                         size_t size = ez_blob_size(action_desc[action], p);
152                         char msg[size+1];
153
154                         msg[0] = action;
155                         ez_blob_encode_raw(action_desc[action], p, msg+1, size);
156                         res = mq_send(q, msg, size+1, msg_pri);
157                 } else {
158                         res = mq_send(q, (char *)&action, 1, msg_pri);
159                 }
160                 if (res) {
161                         perror("msg_send");
162                 }
163         } else {
164                 errno = EINVAL;
165                 perror("msg_send: invalid action");
166         }
167
168         return res;
169 }
170
171 /**
172  * Recieve a message, blocking until one is available.
173  *
174  * @return the (decoded) message, check actionp to determine it's
175  * format.  This may be NULL for no-payload messages or on fatal
176  * error, check the action.
177  */
178 void *notify_msg_receive(mqd_t q, enum notify_action *actionp, unsigned int *msg_pri) {
179         char msg[msg_size];
180
181         while (1) {
182                 ssize_t size = mq_receive(q, msg, msg_size, msg_pri);
183
184                 if (size >= 1) {
185                         enum notify_action action = msg[0];
186
187                         if (action < NOTIFY_SIZEOF) {
188                                 *actionp = action;
189
190                                 if (action_desc[action]) {
191                                         void *p = ez_blob_decode(action_desc[action], msg+1, size-1);
192
193                                         if (p)
194                                                 return p;
195                                 } else {
196                                         return NULL;
197                                 }
198                         }
199                 } else if (size == 0) {
200                         // ignore  or quit?
201                 } else {
202                         if (errno == EINTR)
203                                 ;
204                         else {
205                                 perror("server: mq_receive");
206                                 break;
207                         }
208                 }
209         }
210
211         *actionp = NOTIFY_QUIT;
212
213         return NULL;
214 }