blob: 5f50d02866232f70a291f50d816412f66028ed63 [file] [log] [blame]
Haiyue Wanga1c50752018-04-03 15:16:09 +08001/* Copyright 2017 - 2018 Intel
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <assert.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <getopt.h>
20#include <limits.h>
21#include <linux/ipmi_bmc.h>
22#include <poll.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <syslog.h>
28#include <sys/mman.h>
29#include <sys/ioctl.h>
30#include <sys/stat.h>
31#include <sys/timerfd.h>
32#include <systemd/sd-bus.h>
33#include <time.h>
34#include <unistd.h>
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080035#include <stdbool.h>
Haiyue Wanga1c50752018-04-03 15:16:09 +080036
37#define DBUS_ERR "org.openbmc.error"
Haiyue Wanga1c50752018-04-03 15:16:09 +080038
39#define LOG_PREFIX "KCSBRIDGED"
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080040#define DBUS_NAME "org.openbmc.HostIpmi."
41#define OBJ_NAME "/org/openbmc/HostIpmi/"
Haiyue Wanga1c50752018-04-03 15:16:09 +080042
Jia, Chunhui37db8192018-12-21 16:12:19 +080043#define DEFAULT_DBUS "org.openbmc.HostIpmi"
44#define DEFAULT_OBJ "/org/openbmc/HostIpmi/1"
45#define DBUS_INTF "org.openbmc.HostIpmi"
46
Haiyue Wanga1c50752018-04-03 15:16:09 +080047#define KCS_TIMEOUT_IN_SEC 5
48#define KCS_MESSAGE_SIZE 256
49
50#define SD_BUS_FD 0
51#define KCS_FD 1
52#define TIMER_FD 2
53#define TOTAL_FDS 3
54
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080055#define NAMEBUFFERLEN 50
56#define OPTMAXLEN (NAMEBUFFERLEN - sizeof(OBJ_NAME) - 1)
57
58char kcsDevice[NAMEBUFFERLEN];
59char busName[NAMEBUFFERLEN];
60char objPath[NAMEBUFFERLEN];
61
Haiyue Wanga1c50752018-04-03 15:16:09 +080062struct kcs_msg_req {
63 uint8_t netfn;
64 uint8_t lun;
65 uint8_t cmd;
66 uint8_t *data;
67 size_t data_len;
68};
69
70struct kcsbridged_context {
71 struct pollfd fds[TOTAL_FDS];
72 struct sd_bus *bus;
73
74 /*
75 * Request and Response Messages are paired together as a Write Transfer
76 * to the BMC to send the request followed by a Read Transfer from the
77 * BMC to get the response.
78 */
79 int expired;
80 uint8_t seqnum;
81 struct kcs_msg_req req;
82};
83
84enum { KCS_LOG_NONE = 0, KCS_LOG_VERBOSE, KCS_LOG_DEBUG };
85
86static void (*kcs_vlog)(int p, const char *fmt, va_list args);
87static int verbosity = KCS_LOG_NONE;
88
89#define MSG_OUT(f_, ...) \
90 do { \
91 if (verbosity != KCS_LOG_NONE) \
92 kcs_log(LOG_INFO, f_, ##__VA_ARGS__); \
93 } while (0)
94
95#define MSG_ERR(f_, ...) \
96 do { \
97 if (verbosity != KCS_LOG_NONE) \
98 kcs_log(LOG_ERR, f_, ##__VA_ARGS__); \
99 } while (0)
100
101static void kcs_log_console(int p, const char *fmt, va_list args)
102{
103 vfprintf(stderr, fmt, args);
104}
105
106__attribute__((format(printf, 2, 3))) static void kcs_log(int p,
107 const char *fmt, ...)
108{
109 va_list args;
110
111 va_start(args, fmt);
112 kcs_vlog(p, fmt, args);
113 va_end(args);
114}
115
116static void kcs_dump_data(uint8_t *data, size_t data_len)
117{
118 size_t i;
119 int str_len;
120 char str[64];
121
122 str_len = 0;
123 for (i = 0; i < data_len; i++) {
124 if (i % 8 == 0) {
125 if (i != 0) {
126 kcs_log(LOG_INFO, "%s\n", str);
127 str_len = 0;
128 }
129 str_len += sprintf(&str[str_len], "\t");
130 }
131
132 str_len += sprintf(&str[str_len], "0x%02x ", data[i]);
133 }
134
135 if (str_len != 0)
136 kcs_log(LOG_INFO, "%s\n", str);
137}
138
139static void kcs_set_timer(struct kcsbridged_context *context, int seconds)
140{
141 struct itimerspec ts;
142 int r;
143
144 ts.it_interval.tv_sec = 0;
145 ts.it_interval.tv_nsec = 0;
146 ts.it_value.tv_nsec = 0;
147 ts.it_value.tv_sec = seconds;
148
149 r = timerfd_settime(context->fds[TIMER_FD].fd, 0, &ts, NULL);
150 if (r == -1)
151 MSG_ERR("Couldn't set timerfd: %s\n", strerror(errno));
152}
153
154static int handle_kcs_request(struct kcsbridged_context *context, uint8_t *msg,
155 size_t msglen)
156{
157 struct kcs_msg_req *req;
158
159 if (msglen < 2) {
160 MSG_ERR("KCS message with a short length (%zd)\n", msglen);
161 return -1;
162 }
163
164 context->expired = 0;
165 context->seqnum++;
166
167 req = &context->req;
168 req->netfn = msg[0] >> 2;
169 req->lun = msg[0] & 0x3;
170 req->cmd = msg[1];
171 req->data = msg + 2;
172 req->data_len = msglen - 2;
173
174 return 0;
175}
176
177static int method_send_message(sd_bus_message *msg, void *userdata,
178 sd_bus_error *err)
179{
180 struct kcsbridged_context *context = userdata;
181 uint8_t netfn, lun, seqnum, cmd, cc;
182 struct kcs_msg_req *req;
183 uint8_t *data;
184 size_t data_sz;
185 int r;
186 uint8_t rsp[KCS_MESSAGE_SIZE];
187
Kun Yi17a2ab72019-02-28 22:35:57 -0800188 if (!context || context->expired)
189 return sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800190
191 r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
192 if (r < 0) {
Kun Yi17a2ab72019-02-28 22:35:57 -0800193 return sd_bus_error_set_errnof(
194 err, -r, "Bad message from seq %u cmd %u", seqnum, cmd);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800195 }
196
197 req = &context->req;
Kun Yi17a2ab72019-02-28 22:35:57 -0800198
Haiyue Wanga1c50752018-04-03 15:16:09 +0800199 if (context->seqnum != seqnum || (req->netfn | 1) != netfn
200 || req->lun != lun || req->cmd != cmd) {
Kun Yi17a2ab72019-02-28 22:35:57 -0800201 MSG_ERR("Mismatch: context seqnum %u netfn %u lun %u cmd %u,"
202 "received seqnum %u netfn %u lun %u cmd %u",
203 context->seqnum, req->netfn, req->lun, req->cmd, seqnum,
204 netfn, lun, cmd);
205 return sd_bus_error_set_errnof(err, EINVAL,
206 "No matching request");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800207 }
208
209 kcs_set_timer(context, 0); /* Stop the timer. */
210
211 r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
Kun Yi17a2ab72019-02-28 22:35:57 -0800212 if (r < 0)
213 return sd_bus_error_set_errnof(
214 err, -r, "Message data read failure seq %u cmd %u",
215 seqnum, cmd);
216
217 if (data_sz > sizeof(rsp) - 3)
218 return sd_bus_error_set_errnof(err, EINVAL, "Bad message data");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800219
220 rsp[0] = (netfn << 2) | (lun & 0x3);
221 rsp[1] = cmd;
222 rsp[2] = cc;
223 if (data_sz)
224 memcpy(rsp + 3, data, data_sz);
225
226 r = write(context->fds[KCS_FD].fd, rsp, 3 + data_sz);
Kun Yi17a2ab72019-02-28 22:35:57 -0800227 if (r < 0)
228 return sd_bus_error_set_errnof(err, errno,
229 "Device file write error");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800230
231 MSG_OUT("Send rsp msg <- seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x cc=0x%02x\n",
232 seqnum, netfn, lun, cmd, cc);
233
234 if (verbosity == KCS_LOG_DEBUG && data_sz != 0)
235 kcs_dump_data(data, data_sz);
236
Kun Yi17a2ab72019-02-28 22:35:57 -0800237 return sd_bus_reply_method_return(msg, "x", 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800238}
239
240static int method_set_sms_atn(sd_bus_message *msg, void *userdata,
241 sd_bus_error *err)
242{
243 struct kcsbridged_context *context = userdata;
244 int r;
245
246 MSG_OUT("Sending SET_SMS_ATN\n");
247
248 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_SET_SMS_ATN);
Kun Yi17a2ab72019-02-28 22:35:57 -0800249 if (r < 0)
250 return sd_bus_error_set_errnof(err, errno,
251 "Couldn't SET_SMS_ATN");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800252
Kun Yi17a2ab72019-02-28 22:35:57 -0800253 return sd_bus_reply_method_return(msg, "x", 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800254}
255
256static int method_clear_sms_atn(sd_bus_message *msg, void *userdata,
257 sd_bus_error *err)
258{
259 struct kcsbridged_context *context = userdata;
260 int r;
261
262 MSG_OUT("Sending CLEAR_SMS_ATN\n");
263
264 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
Kun Yi17a2ab72019-02-28 22:35:57 -0800265 if (r < 0)
266 return sd_bus_error_set_errnof(err, errno,
267 "Couldn't CLEAR_SMS_ATN");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800268
Kun Yi17a2ab72019-02-28 22:35:57 -0800269 return sd_bus_reply_method_return(msg, "x", 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800270}
271
272static int method_force_abort(sd_bus_message *msg, void *userdata,
273 sd_bus_error *err)
274{
275 struct kcsbridged_context *context = userdata;
276 int r;
277
278 MSG_OUT("Sending FORCE_ABORT\n");
279
280 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_FORCE_ABORT);
Kun Yi17a2ab72019-02-28 22:35:57 -0800281 if (r < 0)
282 return sd_bus_error_set_errnof(err, errno,
283 "Couldn't FORCE_ABORT");
Haiyue Wanga1c50752018-04-03 15:16:09 +0800284
Kun Yi17a2ab72019-02-28 22:35:57 -0800285 return sd_bus_reply_method_return(msg, "x", 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800286}
287
288static int dispatch_sd_bus(struct kcsbridged_context *context)
289{
290 int r = 0;
291
292 if (context->fds[SD_BUS_FD].revents) {
James Feist980b6f12019-01-18 09:47:03 -0800293 // docs say to call this in a loop until no events are left
294 // to be processed
295 do {
296 r = sd_bus_process(context->bus, NULL);
297 } while (r > 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800298 }
299
300 return r;
301}
302
303static int dispatch_timer(struct kcsbridged_context *context)
304{
305 if (context->fds[TIMER_FD].revents & POLLIN) {
306 struct kcs_msg_req *req;
307 uint8_t rsp[3];
308
309 MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
310
311 context->expired = 1;
312
313 req = &context->req;
314 rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
315 rsp[1] = req->cmd;
316 rsp[2] = 0xce; /* Command response could not be provided */
317 if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
Kun Yi17a2ab72019-02-28 22:35:57 -0800318 MSG_ERR("Failed to send the timeout response: %s\n",
319 strerror(errno));
Haiyue Wanga1c50752018-04-03 15:16:09 +0800320 }
321
322 return 0;
323}
324
325static int dispatch_kcs(struct kcsbridged_context *context)
326{
327 struct kcs_msg_req *req = &context->req;
328 sd_bus_message *msg;
329 int r = 0, len;
330 uint8_t data[KCS_MESSAGE_SIZE];
331
332 if (!(context->fds[KCS_FD].revents & POLLIN))
333 goto out;
334
335 len = read(context->fds[KCS_FD].fd, data, sizeof(data));
336 if (len < 0 || handle_kcs_request(context, data, len))
337 goto out;
338
Jia, Chunhui9e382a12019-01-08 15:05:24 +0800339 r = sd_bus_message_new_signal(context->bus, &msg, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800340 "ReceivedMessage");
341 if (r < 0) {
342 MSG_ERR("Failed to create signal: %s\n", strerror(-r));
343 goto out;
344 }
345
346 r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
347 req->lun, req->cmd);
348 if (r < 0) {
349 MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
350 goto bail;
351 }
352
353 r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
354 if (r < 0) {
355 MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
356 goto bail;
357 }
358
359 r = sd_bus_send(context->bus, msg, NULL);
360 if (r < 0) {
361 MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
362 goto bail;
363 }
364
365 kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
366
367 MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
368 context->seqnum, req->netfn, req->lun, req->cmd);
369
370 if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
371 kcs_dump_data(req->data, req->data_len);
372
373bail:
374 sd_bus_message_unref(msg);
375out:
376 return r;
377}
378
379static void usage(const char *name)
380{
381 fprintf(stderr,
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800382 "Usage %s [--v[v] | --syslog] --i <ID> --d <DEVICE>\n"
383 "--v Be verbose\n"
384 "--vv Be verbose and dump entire messages\n"
385 "--s, --syslog Log output to syslog (pointless without --verbose)\n"
Jia, Chunhui37db8192018-12-21 16:12:19 +0800386 "--i, --instanceid <ID> instance id (string type) optional\n"
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800387 "--d, --device <DEVICE> Use <DEVICE> file.\n\n",
Haiyue Wanga1c50752018-04-03 15:16:09 +0800388 name);
389}
390
391static const sd_bus_vtable ipmid_vtable[] = {
392 SD_BUS_VTABLE_START(0),
393 SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
394 SD_BUS_VTABLE_UNPRIVILEGED),
395 SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
396 SD_BUS_VTABLE_UNPRIVILEGED),
397 SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
398 SD_BUS_VTABLE_UNPRIVILEGED),
399 SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
400 SD_BUS_VTABLE_UNPRIVILEGED),
401 SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
402 SD_BUS_VTABLE_END};
403
404int main(int argc, char *argv[])
405{
406 struct kcsbridged_context *context;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800407 const char *name = argv[0];
Jia, Chunhui37db8192018-12-21 16:12:19 +0800408 bool deviceOptFlag = false;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800409 int opt, polled, r;
410 static const struct option long_options[] = {
411 {"device", required_argument, 0, 'd'},
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800412 {"instanceid", required_argument, 0, 'i'},
Haiyue Wanga1c50752018-04-03 15:16:09 +0800413 {"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
414 {"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
415 {"syslog", no_argument, 0, 's'},
416 {0, 0, 0, 0}};
417
418 context = calloc(1, sizeof(*context));
419 if (!context) {
420 fprintf(stderr, "OOM!\n");
421 return -1;
422 }
423
Jia, Chunhui37db8192018-12-21 16:12:19 +0800424 snprintf(busName, NAMEBUFFERLEN, "%s", DEFAULT_DBUS);
425 snprintf(objPath, NAMEBUFFERLEN, "%s", DEFAULT_OBJ);
426
Haiyue Wanga1c50752018-04-03 15:16:09 +0800427 kcs_vlog = &kcs_log_console;
428 while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
429 switch (opt) {
430 case 0:
431 break;
432
433 case 'd':
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800434 snprintf(kcsDevice, NAMEBUFFERLEN, "%s", optarg);
435 deviceOptFlag = true;
436 break;
437
438 case 'i':
439 if (sizeof(*optarg) > OPTMAXLEN) {
440 fprintf(stderr, "ID is too long!\n");
441 exit(EXIT_FAILURE);
442 }
443 if ((NULL != strstr(optarg, "."))
444 || (NULL != strstr(optarg, "/"))) {
445 fprintf(stderr, "invalid ID!\n");
446 exit(EXIT_FAILURE);
447 }
448 snprintf(busName, NAMEBUFFERLEN, "%s%s", DBUS_NAME,
449 optarg);
450 snprintf(objPath, NAMEBUFFERLEN, "%s%s", OBJ_NAME,
451 optarg);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800452 break;
453
454 case 's':
455 if (kcs_vlog != &vsyslog) {
456 openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
457 kcs_vlog = &vsyslog;
458 }
459 break;
460
461 default:
462 usage(name);
463 exit(EXIT_FAILURE);
464 }
465 }
466
Jia, Chunhui37db8192018-12-21 16:12:19 +0800467 if (false == deviceOptFlag) {
Haiyue Wanga1c50752018-04-03 15:16:09 +0800468 usage(name);
Jia, Chunhui37db8192018-12-21 16:12:19 +0800469 MSG_OUT("Flag: device %d \n", deviceOptFlag);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800470 exit(EXIT_FAILURE);
471 }
472
473 if (verbosity == KCS_LOG_VERBOSE)
474 MSG_OUT("Verbose logging\n");
475 else if (verbosity == KCS_LOG_DEBUG)
476 MSG_OUT("Debug logging\n");
477
478 MSG_OUT("Starting\n");
479 r = sd_bus_default_system(&context->bus);
480 if (r < 0) {
481 MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
482 goto finish;
483 }
484
485 MSG_OUT("Registering dbus methods/signals\n");
Jia, Chunhui37db8192018-12-21 16:12:19 +0800486 r = sd_bus_add_object_vtable(context->bus, NULL, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800487 ipmid_vtable, context);
488 if (r < 0) {
489 MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
490 goto finish;
491 }
492
Jia, Chunhui37db8192018-12-21 16:12:19 +0800493 MSG_OUT("Requesting dbus : %s objpath:%s \n", busName, objPath);
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800494 r = sd_bus_request_name(context->bus, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800495 SD_BUS_NAME_ALLOW_REPLACEMENT
496 | SD_BUS_NAME_REPLACE_EXISTING);
497 if (r < 0) {
498 MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
499 goto finish;
500 }
501
502 MSG_OUT("Getting dbus file descriptors\n");
503 context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
504 if (context->fds[SD_BUS_FD].fd < 0) {
505 r = -errno;
506 MSG_OUT("Couldn't get the bus file descriptor: %s\n",
507 strerror(errno));
508 goto finish;
509 }
510
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800511 MSG_OUT("Opening %s\n", kcsDevice);
512 context->fds[KCS_FD].fd = open(kcsDevice, O_RDWR | O_NONBLOCK);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800513 if (context->fds[KCS_FD].fd < 0) {
514 r = -errno;
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800515 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", kcsDevice,
516 strerror(errno));
Haiyue Wanga1c50752018-04-03 15:16:09 +0800517 goto finish;
518 }
519
520 MSG_OUT("Creating timer fd\n");
521 context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
522 if (context->fds[TIMER_FD].fd < 0) {
523 r = -errno;
524 MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
525 goto finish;
526 }
527 context->fds[SD_BUS_FD].events = POLLIN;
528 context->fds[KCS_FD].events = POLLIN;
529 context->fds[TIMER_FD].events = POLLIN;
530
531 MSG_OUT("Entering polling loop\n");
532
533 while (1) {
534 polled = poll(context->fds, TOTAL_FDS, 5000);
535 if (polled == 0)
536 continue;
537 if (polled < 0) {
538 r = -errno;
539 MSG_ERR("Error from poll(): %s\n", strerror(errno));
540 goto finish;
541 }
542
543 r = dispatch_sd_bus(context);
544 if (r < 0) {
545 MSG_ERR("Error handling dbus event: %s\n",
546 strerror(-r));
547 goto finish;
548 }
549 r = dispatch_kcs(context);
550 if (r < 0) {
551 MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
552 goto finish;
553 }
554 r = dispatch_timer(context);
555 if (r < 0) {
556 MSG_ERR("Error handling timer event: %s\n",
557 strerror(-r));
558 goto finish;
559 }
560 }
561
562finish:
563 close(context->fds[KCS_FD].fd);
564 close(context->fds[TIMER_FD].fd);
565 sd_bus_unref(context->bus);
566 free(context);
567
568 return r;
569}