Fix warnings on 64 bits
[platform/upstream/lightmediascanner.git] / src / bin / lightmediascannerctl.c
1 #include <gio/gio.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <time.h>
6
7 struct app {
8     int ret;
9     int argc;
10     char **argv;
11     void (*action)(struct app *app);
12     GDBusProxy *proxy;
13     GMainLoop *loop;
14     GTimer *timer;
15 };
16
17 static void
18 print_server(GDBusProxy *proxy)
19 {
20     char **props, **itr;
21     char *nameowner;
22
23     nameowner = g_dbus_proxy_get_name_owner(proxy);
24     if (!nameowner) {
25         puts("Server is not running.");
26         return;
27     }
28
29     printf("Server at %s\n", nameowner);
30     props = g_dbus_proxy_get_cached_property_names(proxy);
31     if (!props)
32         return;
33
34     for (itr = props; *itr != NULL; itr++) {
35         GVariant *value = g_dbus_proxy_get_cached_property(proxy, *itr);
36         char *str = g_variant_print(value, TRUE);
37         printf("\t%s = %s\n", *itr, str);
38         g_variant_unref(value);
39         g_free(str);
40     }
41     g_strfreev(props);
42     g_free(nameowner);
43 }
44
45
46 static void
47 do_status(struct app *app)
48 {
49     print_server(app->proxy);
50     g_main_loop_quit(app->loop);
51     app->ret = EXIT_SUCCESS;
52 }
53
54 static void
55 on_properties_changed(GDBusProxy *proxy, GVariant *changed, const char *const *invalidated, gpointer user_data)
56 {
57     struct app *app = user_data;
58
59     printf("%015.3f --- Properties Changed ---\n",
60            g_timer_elapsed(app->timer, NULL));
61
62     if (g_variant_n_children(changed) > 0) {
63         GVariantIter *itr;
64         const char *prop;
65         GVariant *value;
66
67         printf("Changed Properties:");
68         g_variant_get(changed, "a{sv}", &itr);
69         while (g_variant_iter_loop(itr, "{&sv}", &prop, &value)) {
70             char *str;
71             str = g_variant_print(value, TRUE);
72             printf(" %s=%s", prop, str);
73             g_free(str);
74         }
75         g_variant_iter_free(itr);
76         printf("\n");
77     }
78
79     if (invalidated[0] != NULL) {
80         const char * const *itr;
81         printf("Invalidated Properties:");
82         for (itr = invalidated; *itr != NULL; itr++)
83             printf(" %s", *itr);
84         printf("\n");
85     }
86
87     print_server(proxy);
88 }
89
90 static gboolean
91 do_delayed_print_server(gpointer data)
92 {
93     GDBusProxy *proxy = data;
94     char **props;
95     char *nameowner;
96
97     nameowner = g_dbus_proxy_get_name_owner(proxy);
98     if (!nameowner) {
99         print_server(proxy);
100         return FALSE;
101     }
102     g_free(nameowner);
103
104     props = g_dbus_proxy_get_cached_property_names(proxy);
105     if (!props) {
106         g_timeout_add(1000, do_delayed_print_server, proxy);
107         return FALSE;
108     }
109
110     g_strfreev(props);
111     print_server(data);
112     return FALSE;
113 }
114
115 static void
116 on_name_owner_notify(GObject *object, GParamSpec *pspec, gpointer user_data)
117 {
118     GDBusProxy *proxy = G_DBUS_PROXY(object);
119     struct app *app = user_data;
120
121     printf("%015.3f --- Name Owner Changed ---\n",
122            g_timer_elapsed(app->timer, NULL));
123     do_delayed_print_server(proxy);
124 }
125
126 static void
127 do_monitor(struct app *app)
128 {
129     app->timer = g_timer_new();
130     g_timer_start(app->timer);
131
132     print_server(app->proxy);
133     g_signal_connect(app->proxy, "g-properties-changed",
134                      G_CALLBACK(on_properties_changed),
135                      app);
136     g_signal_connect(app->proxy, "notify::g-name-owner",
137                      G_CALLBACK(on_name_owner_notify),
138                      app);
139 }
140
141 static void
142 on_properties_changed_check_lock(GDBusProxy *proxy, GVariant *changed, const char *const *invalidated, gpointer user_data)
143 {
144     struct app *app = user_data;
145     gboolean lost_lock = FALSE;
146
147     if (g_variant_n_children(changed) > 0) {
148         GVariantIter *itr;
149         const char *prop;
150         GVariant *value;
151
152         g_variant_get(changed, "a{sv}", &itr);
153         while (g_variant_iter_loop(itr, "{&sv}", &prop, &value)) {
154             if (strcmp(prop, "WriteLocked") == 0) {
155                 if (!g_variant_get_boolean(value))
156                     lost_lock = TRUE;
157                 break;
158             }
159         }
160         g_variant_iter_free(itr);
161     }
162
163     if (invalidated[0] != NULL) {
164         const char * const *itr;
165         for (itr = invalidated; *itr != NULL; itr++) {
166             if (strcmp(*itr, "WriteLocked") == 0) {
167                 lost_lock = TRUE;
168                 break;
169             }
170         }
171     }
172
173     if (lost_lock) {
174         fputs("Lost lock, exit.\n", stderr);
175         app->ret = EXIT_FAILURE;
176         g_main_loop_quit(app->loop);
177     }
178 }
179
180 static void
181 do_write_lock(struct app *app)
182 {
183     GVariant *ret;
184     GError *error = NULL;
185     char *nameowner;
186
187     nameowner = g_dbus_proxy_get_name_owner(app->proxy);
188     if (!nameowner) {
189         fputs("Server is not running, cannot get write lock!\n", stderr);
190         app->ret = EXIT_FAILURE;
191         g_main_loop_quit(app->loop);
192         return;
193     }
194
195     printf("Server at %s, try to get write lock\n", nameowner);
196     g_free(nameowner);
197
198     g_signal_connect(app->proxy, "g-properties-changed",
199                      G_CALLBACK(on_properties_changed_check_lock),
200                      app);
201
202     ret = g_dbus_proxy_call_sync(app->proxy, "RequestWriteLock",
203                                  g_variant_new("()"),
204                                  G_DBUS_CALL_FLAGS_NONE, -1, NULL,
205                                  &error);
206
207     if (!ret) {
208         fprintf(stderr, "Could not get write lock: %s\n", error->message);
209         g_error_free(error);
210         app->ret = EXIT_FAILURE;
211         g_main_loop_quit(app->loop);
212         return;
213     }
214
215     g_variant_unref(ret);
216     puts("Got write lock, close program to release it.");
217 }
218
219 static void
220 on_properties_changed_check_scan(GDBusProxy *proxy, GVariant *changed, const char *const *invalidated, gpointer user_data)
221 {
222     struct app *app = user_data;
223
224     if (g_variant_n_children(changed) > 0) {
225         GVariantIter *itr;
226         const char *prop;
227         GVariant *value;
228
229         g_variant_get(changed, "a{sv}", &itr);
230         while (g_variant_iter_loop(itr, "{&sv}", &prop, &value)) {
231             if (strcmp(prop, "IsScanning") == 0) {
232                 if (g_variant_get_boolean(value) && !app->timer) {
233                     app->timer = g_timer_new();
234                     g_timer_start(app->timer);
235                 } else if (!g_variant_get_boolean(value) && app->timer) {
236                     g_timer_stop(app->timer);
237                     app->ret = EXIT_SUCCESS;
238                     g_main_loop_quit(app->loop);
239                 }
240                 g_variant_unref(value);
241                 break;
242             } else
243                 g_variant_unref(value);
244         }
245         g_variant_iter_free(itr);
246     }
247
248     if (invalidated[0] != NULL) {
249         const char * const *itr;
250         for (itr = invalidated; *itr != NULL; itr++) {
251             if (strcmp(*itr, "IsScanning") == 0) {
252                 fputs("Lost server, exit.\n", stderr);
253                 app->ret = EXIT_FAILURE;
254                 g_main_loop_quit(app->loop);
255                 break;
256             }
257         }
258     }
259 }
260
261 static void
262 on_signal(GDBusProxy *proxy, gchar *sender, gchar *signal, GVariant *params, gpointer user_data)
263 {
264     if (g_str_equal(signal, "ScanProgress")) {
265         const gchar *category = NULL, *path = NULL;
266         guint64 uptodate = 0, processed = 0, deleted = 0,
267             skipped = 0, errors = 0;
268
269         g_variant_get(params, "(&s&sttttt)",
270                       &category,
271                       &path,
272                       &uptodate,
273                       &processed,
274                       &deleted,
275                       &skipped,
276                       &errors);
277
278         printf("Scan Progress %s:%s uptodate=%"G_GUINT64_FORMAT", "
279                "processed=%"G_GUINT64_FORMAT", deleted=%"G_GUINT64_FORMAT", "
280                "skipped=%"G_GUINT64_FORMAT", errors=%"G_GUINT64_FORMAT"\n",
281                category, path, uptodate, processed, deleted, skipped, errors);
282     }
283 }
284
285 static void
286 populate_scan_params(gpointer key, gpointer value, gpointer user_data)
287 {
288     const char *category = key;
289     const GArray *paths = value;
290     GVariantBuilder *builder = user_data;
291     GVariantBuilder *sub;
292     char **itr;
293
294     sub = g_variant_builder_new(G_VARIANT_TYPE("as"));
295     for (itr = (char **)paths->data; *itr != NULL; itr++)
296         g_variant_builder_add(sub, "s", *itr);
297
298     g_variant_builder_add(builder, "{sv}", category, g_variant_builder_end(sub));
299     g_variant_builder_unref(sub);
300 }
301
302 static void
303 do_free_array(gpointer data)
304 {
305     g_array_free(data, TRUE);
306 }
307
308 static void
309 do_scan(struct app *app)
310 {
311     GVariantBuilder *builder;
312     GVariant *ret;
313     GError *error = NULL;
314     char *nameowner;
315     int i;
316     GHashTable *categories;
317
318     nameowner = g_dbus_proxy_get_name_owner(app->proxy);
319     if (!nameowner) {
320         fputs("Server is not running, cannot start scan!\n", stderr);
321         app->ret = EXIT_FAILURE;
322         g_main_loop_quit(app->loop);
323         return;
324     }
325
326     printf("Server at %s, try to start scan\n", nameowner);
327     g_free(nameowner);
328
329     g_signal_connect(app->proxy, "g-properties-changed",
330                      G_CALLBACK(on_properties_changed_check_scan),
331                      app);
332     g_signal_connect(app->proxy, "g-signal",
333                      G_CALLBACK(on_signal),
334                      app);
335
336     categories = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, do_free_array);
337     for (i = 0; i < app->argc; i++) {
338         GArray *arr;
339         char *arg = app->argv[i];
340         char *sep = strchr(arg, ':');
341         char *path;
342
343
344         if (!sep) {
345             fprintf(stderr, "Ignored scan parameter: invalid format '%s'\n",
346                     arg);
347             continue;
348         }
349
350         *sep = '\0';
351         path = sep + 1;
352
353         arr = g_hash_table_lookup(categories, arg);
354         if (!arr) {
355             arr = g_array_new(TRUE, FALSE, sizeof(char *));
356             g_hash_table_insert(categories, arg, arr);
357         }
358
359         if (path[0])
360             g_array_append_val(arr, path);
361     }
362
363     builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
364     g_hash_table_foreach(categories, populate_scan_params, builder);
365
366     ret = g_dbus_proxy_call_sync(app->proxy, "Scan",
367                                  g_variant_new("(a{sv})", builder),
368                                  G_DBUS_CALL_FLAGS_NONE, -1, NULL,
369                                  &error);
370     g_variant_builder_unref(builder);
371     g_hash_table_destroy(categories);
372
373     if (!ret) {
374         fprintf(stderr, "Could not start scan: %s\n", error->message);
375         g_error_free(error);
376         app->ret = EXIT_FAILURE;
377         g_main_loop_quit(app->loop);
378         return;
379     }
380
381     g_variant_unref(ret);
382 }
383
384 static void
385 do_stop(struct app *app)
386 {
387     GVariant *ret;
388     GError *error = NULL;
389     char *nameowner;
390
391     nameowner = g_dbus_proxy_get_name_owner(app->proxy);
392     if (!nameowner) {
393         fputs("Server is not running, cannot stop scan!\n", stderr);
394         app->ret = EXIT_FAILURE;
395         g_main_loop_quit(app->loop);
396         return;
397     }
398
399     printf("Server at %s, try to stop scan\n", nameowner);
400     g_free(nameowner);
401
402     ret = g_dbus_proxy_call_sync(app->proxy, "Stop",
403                                  g_variant_new("()"),
404                                  G_DBUS_CALL_FLAGS_NONE, -1, NULL,
405                                  &error);
406
407     if (!ret) {
408         fprintf(stderr, "Could not stop scan: %s\n", error->message);
409         g_error_free(error);
410         app->ret = EXIT_FAILURE;
411         g_main_loop_quit(app->loop);
412         return;
413     }
414
415     app->ret = EXIT_SUCCESS;
416     g_main_loop_quit(app->loop);
417     g_variant_unref(ret);
418 }
419
420 static gboolean
421 do_action(gpointer data)
422 {
423     struct app *app = data;
424     app->action(app);
425     return FALSE;
426 }
427
428 static void
429 print_help(const char *prog)
430 {
431     printf("Usage:\n"
432            "\t%s <action>\n"
433            "\n"
434            "Action is one of:\n"
435            "\tstatus          print server properties and exit.\n"
436            "\tmonitor         monitor server and its properties.\n"
437            "\twrite-lock      try to get a write-lock and keep it while running.\n"
438            "\tscan [params]   start scan. May receive parameters as a series of \n"
439            "\t                CATEGORY:PATH to limit scan.\n"
440            "\tstop            stop ongoing scan.\n"
441            "\thelp            this message.\n"
442            "\n",
443            prog);
444 }
445
446 int
447 main(int argc, char *argv[])
448 {
449     GError *error = NULL;
450     struct app app;
451     int i;
452
453     if (argc < 2) {
454         fprintf(stderr, "Missing action, see --help.\n");
455         return EXIT_FAILURE;
456     }
457
458     for (i = 1; i < argc; i++) {
459         if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
460             print_help(argv[0]);
461             return EXIT_SUCCESS;
462         }
463     }
464
465     if (strcmp(argv[1], "status") == 0)
466         app.action = do_status;
467     else if (strcmp(argv[1], "monitor") == 0)
468         app.action = do_monitor;
469     else if (strcmp(argv[1], "write-lock") == 0)
470         app.action = do_write_lock;
471     else if (strcmp(argv[1], "scan") == 0)
472         app.action = do_scan;
473     else if (strcmp(argv[1], "stop") == 0)
474         app.action = do_stop;
475     else if (strcmp(argv[1], "help") == 0) {
476         print_help(argv[0]);
477         return EXIT_SUCCESS;
478     } else {
479         fprintf(stderr, "Unknown action '%s', see --help.\n", argv[1]);
480         return EXIT_FAILURE;
481     }
482
483     app.timer = NULL;
484     app.loop = g_main_loop_new(NULL, FALSE);
485     app.proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
486                                               G_DBUS_PROXY_FLAGS_NONE,
487                                               NULL,
488                                               "org.lightmediascanner",
489                                               "/org/lightmediascanner/Scanner1",
490                                               "org.lightmediascanner.Scanner1",
491                                               NULL,
492                                               &error);
493     if (error) {
494         g_error("Could not create proxy: %s", error->message);
495         g_error_free(error);
496         return EXIT_FAILURE;
497     }
498
499     app.argc = argc - 2;
500     app.argv = argv + 2;
501     app.ret = EXIT_SUCCESS;
502
503     g_idle_add(do_action, &app);
504
505     g_main_loop_run(app.loop);
506     g_object_unref(app.proxy);
507     g_main_loop_unref(app.loop);
508
509     if (app.timer) {
510         printf("Elapsed time: %0.3f seconds\n",
511                g_timer_elapsed(app.timer, NULL));
512         g_timer_destroy(app.timer);
513     }
514
515     return app.ret;
516 }