Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Example for notification library which reloads KDB when Elektra's
5 : * configuration (e.g. mount points or global plugins) has changed.
6 : * This example also shows how to pass user data using elektraIo*GetData().
7 : *
8 : * Requires:
9 : * - io_glib binding
10 : * - Transport plugins (e.g. kdb global-mount zeromqsend zeromqrecv && kdb run-hub-zeromq)
11 : *
12 : * Relevant keys for this example:
13 : * - /sw/example/notification/#0/current/value: Set to any integer value
14 : * Add additional transport plugins and remove the original pair afterwards or
15 : * mount a file which sets the key above to a different value and unmount it again
16 : *
17 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
18 : *
19 : */
20 :
21 : #include <kdb.h>
22 : #include <kdbhelper.h> // elektraFree
23 : #include <kdbio.h> // I/O binding functions (elektraIo*)
24 : #include <kdbio/glib.h> // I/O binding constructor for glib (elektraIoGlibNew)
25 : #include <kdbnotification.h> // notification functions
26 :
27 : #include <glib-unix.h> // g_unix_signal_add()
28 : #include <glib.h> // glib functions
29 :
30 : #include <signal.h> // signal()
31 : #include <stdio.h> // printf() & co
32 : #include <stdlib.h> // exit()
33 :
34 : #define TWO_SECONDS 2000
35 : #define RELOAD_INTERVAL 100
36 :
37 : /**
38 : * Data container for this example to demo
39 : * usage of the elektraIo*GetData() functions.
40 : *
41 : * Members could also be globals.
42 : */
43 : typedef struct ExampleUserData
44 : {
45 : GMainLoop * loop;
46 : KDB * kdb;
47 : Key * parentKey;
48 : KeySet * config;
49 : ElektraIoInterface * binding;
50 : Key * intKeyToWatch;
51 : int valueToPrint;
52 : ElektraIoTimerOperation * timer;
53 : ElektraIoTimerOperation * reload;
54 : } ExampleUserData;
55 :
56 : static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context);
57 :
58 : /**
59 : * Initializes KDB on first call and performs cleanup before initialization on
60 : * subsequent calls.
61 : *
62 : * @param timerOp unused
63 : */
64 0 : static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
65 : {
66 0 : ExampleUserData * data = (ExampleUserData *) elektraIoTimerGetData (timerOp);
67 :
68 0 : int didReload = 0;
69 :
70 : // Stop reload task
71 0 : elektraIoTimerSetEnabled (data->reload, 0);
72 0 : elektraIoBindingUpdateTimer (data->reload);
73 :
74 0 : if (data->kdb != NULL)
75 : {
76 : // Cleanup notifications and close KDB
77 0 : elektraNotificationClose (data->kdb);
78 0 : kdbClose (data->kdb, data->parentKey);
79 0 : didReload = 1;
80 : }
81 :
82 0 : data->kdb = kdbOpen (data->parentKey);
83 0 : if (data->kdb == NULL)
84 : {
85 0 : printf ("could not open KDB, aborting\n");
86 0 : exit (1);
87 : }
88 :
89 0 : elektraIoSetBinding (data->kdb, data->binding);
90 :
91 0 : int result = elektraNotificationOpen (data->kdb);
92 0 : if (!result)
93 : {
94 0 : printf ("could not init notification, aborting\n");
95 0 : exit (1);
96 : }
97 :
98 0 : result = elektraNotificationRegisterInt (data->kdb, data->intKeyToWatch, &data->valueToPrint);
99 0 : if (!result)
100 : {
101 0 : printf ("could not register variable, aborting\n");
102 0 : exit (1);
103 : }
104 :
105 0 : Key * elektraKey = keyNew ("/elektra", KEY_END);
106 0 : if (!elektraNotificationRegisterCallbackSameOrBelow (data->kdb, elektraKey, elektraChangedCallback, data))
107 : {
108 0 : printf ("could not register for changes to Elektra's configuration, aborting\n");
109 0 : exit (1);
110 : }
111 0 : keyDel (elektraKey);
112 :
113 : // Get configuration
114 0 : kdbGet (data->kdb, data->config, data->parentKey);
115 :
116 0 : if (didReload)
117 : {
118 0 : printf ("KDB reloaded.\n");
119 : }
120 0 : }
121 :
122 0 : static gboolean onSIGNAL (gpointer user_data)
123 : {
124 0 : ExampleUserData * data = (ExampleUserData *) user_data;
125 : // Cleanup
126 0 : elektraIoBindingRemoveTimer (data->timer);
127 0 : elektraFree (data->timer);
128 0 : elektraIoBindingRemoveTimer (data->reload);
129 0 : elektraFree (data->reload);
130 0 : elektraNotificationClose (data->kdb);
131 0 : kdbClose (data->kdb, data->parentKey);
132 0 : elektraIoBindingCleanup (data->binding);
133 :
134 0 : g_main_loop_quit (data->loop);
135 0 : return FALSE;
136 : }
137 :
138 : /**
139 : * This function is called whenever Elektra's configuration has changed.
140 : * Since cannot call elektraNotificationClose() here we start a timer operation
141 : * which allows us to reload KDB in the next main loop iteration.
142 : *
143 : * @param changedKey unused
144 : * @param context unused
145 : */
146 0 : static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context)
147 : {
148 0 : printf ("\nElektra's configuration has changed.\n");
149 :
150 : // Enable operation to reload KDB as soon as possible
151 0 : ExampleUserData * data = (ExampleUserData *) context;
152 0 : elektraIoTimerSetEnabled (data->reload, 1);
153 0 : elektraIoBindingUpdateTimer (data->reload);
154 0 : }
155 :
156 0 : static void printVariable (ElektraIoTimerOperation * timerOp)
157 : {
158 : // int value = *(int *) elektraIoTimerGetData (timerOp);
159 0 : ExampleUserData * data = (ExampleUserData *) elektraIoTimerGetData (timerOp);
160 0 : printf ("\nMy integer value is %d\n", data->valueToPrint);
161 0 : }
162 :
163 0 : int main (void)
164 : {
165 0 : ExampleUserData * data = elektraCalloc (sizeof (*data));
166 0 : if (data == NULL)
167 : {
168 0 : printf ("elektraCalloc failed");
169 0 : return 1;
170 : }
171 :
172 : // Create glib main loop
173 0 : GMainContext * context = NULL; // use default context
174 0 : data->loop = g_main_loop_new (context, 0);
175 0 : data->binding = elektraIoGlibNew (context);
176 :
177 : // Signal Handling
178 0 : g_unix_signal_add (SIGINT, onSIGNAL, data);
179 :
180 0 : data->config = ksNew (20, KS_END);
181 0 : data->parentKey = keyNew ("/sw/example/notification/#0/current", KEY_END);
182 0 : data->intKeyToWatch = keyNew ("/sw/example/notification/#0/current/value", KEY_END);
183 :
184 : // Setup timer that repeatedly prints the variable
185 0 : data->timer = elektraIoNewTimerOperation (TWO_SECONDS, 1, printVariable, data);
186 0 : elektraIoBindingAddTimer (data->binding, data->timer);
187 :
188 : // Setup timer for reloading Elektra's configuration
189 0 : data->reload = elektraIoNewTimerOperation (RELOAD_INTERVAL, 0, initKdb, data);
190 0 : elektraIoBindingAddTimer (data->binding, data->reload);
191 :
192 0 : printf ("Reloading Notification Example Application\n");
193 0 : printf ("Please note that notification transport plugins are required see\n"
194 : " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n");
195 0 : printf ("- Set \"%s\" to any integer value\n", keyName (data->intKeyToWatch));
196 0 : printf ("- Try to add additional transport plugins and remove the original pair afterwards\n");
197 0 : printf ("- Mount a file which sets the key above to a different value and unmount it\n");
198 0 : printf ("Send SIGINT (Ctl+C) to exit.\n\n");
199 :
200 : // Initialize KDB
201 0 : initKdb (data->reload);
202 0 : printVariable (data->timer); // "value" was automatically updated
203 :
204 0 : g_main_loop_run (data->loop);
205 :
206 0 : g_main_loop_unref (data->loop);
207 :
208 0 : ksDel (data->config);
209 0 : keyDel (data->intKeyToWatch);
210 0 : keyDel (data->parentKey);
211 0 : elektraFree (data);
212 0 : printf ("cleanup done!\n");
213 : }
|