]> www.average.org Git - pulsecounter.git/blob - linux/pulsecounter.c
adjust persist struct
[pulsecounter.git] / linux / pulsecounter.c
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdbool.h>
5 #include <time.h>
6 #include <syslog.h>
7
8 #include <glib.h>
9
10 #include <lib/bluetooth.h>
11 #include <lib/hci.h>
12 #include <lib/hci_lib.h>
13 #include <lib/sdp.h>
14 #include "lib/uuid.h"
15
16 #include "src/shared/util.h"
17 #include "attrib/att.h"
18 #include "btio/btio.h"
19 #include "attrib/gattrib.h"
20 #include "attrib/gatt.h"
21
22 #include "dbstore.h"
23
24 GIOChannel *gatt_connect(const char *src, const char *dst,
25                         const char *dst_type, const char *sec_level,
26                         int psm, int mtu, BtIOConnect connect_cb,
27                         GError **gerr);
28
29 static char *opt_src = NULL;
30 static char *opt_dst = NULL;
31 static char *opt_dst_type = NULL;
32 static int opt_mtu = 0;
33 static int opt_psm = 0;
34 static char *opt_sec_level = NULL;
35 static char *opt_dbconffile = NULL;
36 static gboolean opt_daemon = FALSE;
37
38 static GMainLoop *event_loop;
39
40 static GOptionEntry options[] = {
41         { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
42                 "Specify local adapter interface", "hciX" },
43         { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
44                 "Specify remote Bluetooth address", "MAC" },
45         { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
46                 "Set LE address type. Default: public", "[public | random]"},
47         { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
48                 "Specify the MTU size", "MTU" },
49         { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
50                 "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
51         { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
52                 "Set security level. Default: low", "[low | medium | high]"},
53         { "dbconfig", 'c', 0, G_OPTION_ARG_FILENAME, &opt_dbconffile,
54                 "Specify file name with database configuration", "cfile"},
55         { "daemon", 'd', 0, G_OPTION_ARG_NONE, &opt_daemon,
56                 "Specify file name with database configuration", "cfile"},
57         { NULL },
58 };
59
60 void local_log_handler(const gchar *log_domain, GLogLevelFlags log_level,
61                         const gchar *message, gpointer log_context)
62 {
63         int syslog_level;
64
65         switch (log_level) {
66         case G_LOG_LEVEL_CRITICAL:      syslog_level = LOG_CRIT;        break;
67         case G_LOG_LEVEL_ERROR:         syslog_level = LOG_ERR;         break;
68         case G_LOG_LEVEL_WARNING:       syslog_level = LOG_WARNING;     break;
69         case G_LOG_LEVEL_MESSAGE:       syslog_level = LOG_NOTICE;      break;
70         case G_LOG_LEVEL_INFO:          syslog_level = LOG_INFO;        break;
71         case G_LOG_LEVEL_DEBUG:         syslog_level = LOG_DEBUG;       break;
72         default:                        syslog_level = LOG_INFO;
73         }
74         if (!log_domain || (log_domain[0] == '\0'))
75                 syslog(syslog_level, "%s", message);
76         else
77                 syslog(syslog_level, "%s: %s", log_domain, message);
78 }
79
80 static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
81                                 gpointer user_data)
82 {
83         g_io_channel_shutdown(chan, FALSE, NULL);
84         g_io_channel_unref(chan);
85         g_main_loop_quit(event_loop);
86         g_info("channel_watcher cleared channel and exiting");
87         return FALSE;
88 }
89
90 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
91 {
92         GAttrib *attrib = user_data;
93         uint8_t *opdu;
94         uint8_t which;
95         uint16_t handle;
96
97         handle = bt_get_le16(&pdu[1]);
98         which = pdu[3];
99         if ((pdu[0] == 0x1b) && (handle == 0x0012) && (len == 9) &&
100             ((which == 1) || (which == 2))) {
101                 uint32_t val = bt_get_le32(&pdu[5]);
102                 g_debug("store: \"%hhu,%u\"\n", which, val);
103                 if (dbstore(which, val))
104                         g_warning("error storing \"%hhu,%u\"\n", which, val);
105         } else {
106                 time_t t;
107                 int i;
108                 struct tm tm;
109                 char buf[64];
110                 char tstr[64];
111
112                 t = time(NULL);
113                 (void)gmtime_r(&t, &tm);
114                 (void)strftime(tstr, sizeof(tstr), "%Y-%m-%d %H:%M:%S", &tm);
115                 for (i = 3; (i < len) && ((i-3) < (sizeof(buf)/3)); i++)
116                         sprintf(buf+strlen(buf), " %02x ", pdu[i]);
117                 g_warning("%s ev %02x hd 0x%04x value: %s",
118                         tstr, pdu[0], handle, buf);
119         }
120 }
121
122 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
123 {
124         GAttrib *attrib;
125         uint16_t mtu;
126         uint16_t cid;
127         GError *gerr = NULL;
128
129         if (err) {
130                 g_warning("%s", err->message);
131                 g_main_loop_quit(event_loop);
132         }
133         bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
134                                 BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
135         if (gerr) {
136                 g_warning("Can't detect MTU, using default: %s",
137                                                         gerr->message);
138                 g_error_free(gerr);
139                 mtu = ATT_DEFAULT_LE_MTU;
140         }
141         if (cid == ATT_CID)
142                 mtu = ATT_DEFAULT_LE_MTU;
143         attrib = g_attrib_new(io, mtu, false);
144         g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
145                                                 events_handler, attrib, NULL);
146         g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
147                                                 events_handler, attrib, NULL);
148         g_info("connect_cb registered events_handler and exiting\n");
149 }
150
151 int main(int argc, char *argv[])
152 {
153         GOptionContext *context;
154         GError *gerr = NULL;
155         GIOChannel *chan;
156         gboolean got_error = FALSE;
157
158         g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
159                           | G_LOG_FLAG_RECURSION, local_log_handler, NULL);
160         opt_dst_type = g_strdup("public");
161         opt_sec_level = g_strdup("low");
162         opt_dbconffile = g_strdup("/etc/pulsecounter.db");
163         context = g_option_context_new(NULL);
164         g_option_context_add_main_entries(context, options, NULL);
165         if (!g_option_context_parse(context, &argc, &argv, &gerr)) {
166                 g_error("%s", gerr->message);
167                 g_clear_error(&gerr);
168                 got_error = TRUE;
169                 goto done;
170         }
171         if (!opt_dst) {
172                 g_error("Destination MAC address must be specified");
173                 got_error = TRUE;
174                 goto done;
175         }
176         if (dbconfig(opt_dbconffile)) {
177                 g_error("Could not parse database configuration file");
178                 got_error = TRUE;
179                 goto done;
180         }
181         if (opt_daemon) daemon(0, 0);
182         while (1) {
183                 chan = gatt_connect(opt_src, opt_dst, opt_dst_type,
184                         opt_sec_level, opt_psm, opt_mtu, connect_cb, &gerr);
185                 if (chan) {
186                         g_io_add_watch(chan, G_IO_HUP, channel_watcher, NULL);
187                         event_loop = g_main_loop_new(NULL, FALSE);
188                         g_main_loop_run(event_loop);
189                         g_main_loop_unref(event_loop);
190                 } else {
191                         g_warning("%s", gerr->message);
192                         g_clear_error(&gerr);
193                         got_error = TRUE;
194                 }
195                 sleep(10);
196         }
197
198 done:
199         g_option_context_free(context);
200         g_free(opt_src);
201         g_free(opt_dst);
202         g_free(opt_sec_level);
203         g_free(opt_dbconffile);
204         if (got_error)
205                 exit(EXIT_FAILURE);
206         else
207                 exit(EXIT_SUCCESS);
208 }