tizen 2.3 release
[kernel/api/system-resource.git] / src / logging / logging.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2012 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /*
20  * @file logging.c
21  *
22  * @desc start logging system for resourced
23  *
24  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <fcntl.h>
31 #include <assert.h>
32 #include <time.h>
33 #include <dirent.h>
34 #include <unistd.h>
35 #include <sys/sysinfo.h>
36 #include <sys/stat.h>
37 #include <glib.h>
38 #include <sys/time.h>
39 #include <sys/resource.h>
40 #include <Ecore.h>
41
42 #include "trace.h"
43 #include "logging-common.h"
44 #include "resourced.h"
45 #include "macro.h"
46 #include "module.h"
47 #include "proc-process.h"
48 #include "proc-main.h"
49 #include "proc_stat.h"
50 #include "logging.h"
51 #include "edbus-handler.h"
52
53 #define BUF_MAX                 1024
54 #define WRITE_INFO_MAX          10
55 #define MAX_PROC_LIST           200
56
57 #define WEBPROCESS_NAME         "WebProcess"
58 #define MAX_PROC_ITEM           200
59 #define INC_PROC_ITEM           10
60 #define COMMIT_INTERVAL         10*60   /* 10 min */
61 #define LOGGING_PTIORITY        -20
62
63 #define SIGNAL_LOGGING_INIT     "LoggingInit"
64 #define SIGNAL_LOGGING_GET      "LoggingGet"
65 #define SIGNAL_LOGGING_UPDATED  "LoggingUpdated"
66 #define PROC_OOM_SCORE_ADJ_PATH "/proc/%d/oom_score_adj"
67
68 struct logging_sub_sys {
69         const char *name;
70         time_t commit_interval;
71         time_t last_commit;
72         struct logging_info_ops *ops;
73 };
74
75 static const struct module_ops logging_modules_ops;
76 static const struct module_ops *logging_ops;
77
78 static int num_log_infos;
79 static bool need_to_update;
80 static GHashTable *logging_proc_list;
81 static GArray *logging_ss_list;
82 static pthread_t        logging_thread  = 0;
83 static pthread_mutex_t  logging_mutex   = PTHREAD_MUTEX_INITIALIZER;
84 static pthread_mutex_t  proc_list_mutex = PTHREAD_MUTEX_INITIALIZER;
85 static pthread_cond_t   logging_cond    = PTHREAD_COND_INITIALIZER;
86
87 static int init_logging_infos(struct logging_infos *info, const char *key,
88         pid_t pid, int oom, time_t now)
89 {
90         int i;
91         int ret;
92
93         if (!info) {
94                 _D("info is null");
95                 return RESOURCED_ERROR_FAIL;
96         }
97
98         info->oom = oom;
99         info->pid = pid;
100         info->running = true;
101
102         for (i = 0; i < logging_ss_list->len; i++) {
103                 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
104                                                 struct logging_sub_sys, i);
105                 ret = ss->ops->init(&(info->stats[i]), pid, oom, now);
106                 if (ret != RESOURCED_ERROR_NONE) {
107                         _E("init logging at %lu", now);
108                         /* not return error, just continue updating */
109                 }
110         }
111
112         return RESOURCED_ERROR_NONE;
113 }
114
115 static void update_logging_infos(struct logging_infos *info,
116         time_t now, unsigned first)
117 {
118         int i;
119         int ret;
120         for (i = 0; i < logging_ss_list->len; i++) {
121                 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
122                                                 struct logging_sub_sys, i);
123                 ret = ss->ops->update(info->stats[i], info->pid, info->oom, now, first);
124                 if (ret != RESOURCED_ERROR_NONE) {
125                         /*
126                          * when update failed, this is because there is no
127                          * running process. So, just update processes running
128                          * state.
129                          */
130                         info->running = false;
131                 }
132         }
133
134         return;
135 }
136
137 static void insert_hash_table(char *key, pid_t pid, int oom)
138 {
139         struct logging_infos *info;
140         void **stats;
141         char *name;
142         time_t now;
143
144         now = time(NULL);
145
146         name = malloc(strlen(key) + 1);
147
148         if (!name) {
149                 _D("memory allocation for name failed");
150                 return;
151         }
152         strcpy(name, key);
153         info = (struct logging_infos *)malloc(sizeof(struct logging_infos));
154
155         if (!info) {
156                 _D("memory allocation for logging_infos failed");
157                 free(name);
158                 return;
159         }
160
161         stats = (void **)malloc(sizeof(void *) * num_log_infos);
162
163         if (!stats) {
164                 _D("memory allocation for log infos fails");
165                 free(name);
166                 free(info);
167                 return;
168         }
169
170         info->stats = stats;
171         init_logging_infos(info, name, pid, oom, now);
172
173         g_hash_table_insert(logging_proc_list, (gpointer) name, (gpointer) info);
174         update_logging_infos(info, now, true);
175         return;
176 }
177
178 static int write_journal(struct logging_sub_sys *pss, int ss_index)
179 {
180         gpointer value;
181         gpointer key;
182         int ret = RESOURCED_ERROR_NONE;
183         char *name;
184         GHashTableIter iter;
185         struct logging_infos *infos;
186         g_hash_table_iter_init(&iter, logging_proc_list);
187
188         while (1) {
189                 ret = pthread_mutex_lock(&proc_list_mutex);
190                 if (ret) {
191                         _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
192                         return ret;
193                 }
194
195                 if (!g_hash_table_iter_next(&iter, &key, &value)) {
196                         ret = pthread_mutex_unlock(&proc_list_mutex);
197                         if (ret) {
198                                 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
199                                 return ret;
200                         }
201                         break;
202                 }
203
204                 name = (char *)key;
205                 infos = (struct logging_infos *)value;
206                 pss->ops->write(name, infos, ss_index);
207                 ret = pthread_mutex_unlock(&proc_list_mutex);
208                 if (ret) {
209                         _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
210                         return ret;
211                 }
212         }
213
214         return RESOURCED_ERROR_NONE;
215 }
216
217 static int write_logging_subsys_info(struct logging_sub_sys *pss, int sindex,
218         time_t now, bool force)
219 {
220
221         if (!force && now < pss->last_commit + pss->commit_interval)
222                 return RESOURCED_ERROR_NONE;
223
224         _D("start write %s subsys, now %lu, last:%lu, interval:%lu",
225                 pss->name, now, pss->last_commit, pss->commit_interval);
226
227         write_journal(pss, sindex);
228
229         pss->last_commit = now;
230         return RESOURCED_ERROR_NONE;
231
232 }
233
234 static int write_logging_infos(bool force)
235 {
236         int i;
237         int ret;
238         time_t now;
239
240         now = time(NULL);
241
242         for (i = 0; i < logging_ss_list->len; i++) {
243                 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
244                                                 struct logging_sub_sys, i);
245                 ret = write_logging_subsys_info(ss, i, now, force);
246                 if (ret != RESOURCED_ERROR_NONE) {
247                         _E("write logging at %lu", now);
248                         /* not return error, just continue updating */
249                 }
250         }
251
252         return RESOURCED_ERROR_NONE;
253 }
254
255 int register_logging_subsystem(const char*name, struct logging_info_ops *ops)
256 {
257         struct logging_sub_sys ss;
258         char *ss_name;
259         time_t now;
260
261         ss_name = malloc(strlen(name)+1);
262
263         if (!ss_name) {
264                 _E("memory allocation for name is failed");
265                 return RESOURCED_ERROR_FAIL;
266         }
267
268         now = time(NULL);
269
270         strcpy(ss_name, name);
271         ss.name = ss_name;
272         ss.commit_interval = COMMIT_INTERVAL;
273         ss.last_commit = now;
274         ss.ops = ops;
275
276         g_array_append_val(logging_ss_list, ss);
277         num_log_infos++;
278
279         return RESOURCED_ERROR_NONE;
280 }
281
282 int update_commit_interval(const char *name, time_t commit_interval)
283 {
284         int i;
285         for (i = 0; i < logging_ss_list->len; i++) {
286                 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
287                                                 struct logging_sub_sys, i);
288                 if (!strcmp(ss->name, name)) {
289                         ss->commit_interval = commit_interval;
290                         _D("%s logging subsystem commit interval updated to %lu",
291                         ss->name, ss->commit_interval);
292                         return RESOURCED_ERROR_NONE;
293                 }
294         }
295
296         _D("%s subsystem update fail, not exist", name);
297         return RESOURCED_ERROR_FAIL;
298 }
299
300 static inline int is_webprocess(char *name)
301 {
302         return !strcmp(name, WEBPROCESS_NAME);
303 }
304
305 static int rename_webprocess(pid_t pgid, char *name)
306 {
307         char webui_name[PROC_NAME_MAX];
308         int ret;
309
310         if ((ret = proc_get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE)
311                 return RESOURCED_ERROR_FAIL;
312
313         strcat(name, ".");
314         strcat(name, webui_name);
315
316         return RESOURCED_ERROR_NONE;
317 }
318
319 static int get_cmdline(pid_t pid, char *cmdline)
320 {
321         char buf[PROC_BUF_MAX];
322         FILE *fp;
323
324         sprintf(buf, "/proc/%d/cmdline", pid);
325         fp = fopen(buf, "r");
326         if (fp == NULL)
327                 return RESOURCED_ERROR_FAIL;
328
329         if (fgets(cmdline, PROC_NAME_MAX-1, fp) == NULL) {
330                 fclose(fp);
331                 return RESOURCED_ERROR_FAIL;
332         }
333         fclose(fp);
334
335         return RESOURCED_ERROR_NONE;
336 }
337
338 static void insert_proc_list(pid_t pid, pid_t pgid, int oom)
339 {
340         int ret = RESOURCED_ERROR_NONE;
341         char name[PROC_NAME_MAX];
342         struct logging_infos *info;
343
344         ret = get_cmdline(pid, name);
345         /*
346          * if cmdline does not exist, remove item from queue
347          * and continue logging remaining items
348          */
349         if (ret != RESOURCED_ERROR_NONE) {
350                 return;
351         }
352
353         if (is_webprocess(name)) {
354                 ret = rename_webprocess(pgid, name);
355                 if (ret != RESOURCED_ERROR_NONE)
356                         return;
357
358         }
359
360         ret = pthread_mutex_lock(&proc_list_mutex);
361         if (ret) {
362                 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
363                 return;
364         }
365
366         info = (struct logging_infos *)
367                 g_hash_table_lookup(logging_proc_list, name);
368
369         /* To Do: handle multiple daemons with the same name */
370         if (!info) {
371                 insert_hash_table(name, pid, oom);
372         } else {
373                 info->running = true;
374                 info->pid = pid;
375                 info->oom = oom;
376         }
377
378         ret = pthread_mutex_unlock(&proc_list_mutex);
379         if (ret) {
380                 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
381                 return;
382         }
383         return;
384 }
385
386 static bool is_running_process(GArray *garray, pid_t pid)
387 {
388         int i;
389         pid_t tpid;
390
391         for (i = 0 ; i < garray->len; i++) {
392                 tpid = g_array_index(garray, pid_t, i);
393                 if (tpid == pid)
394                         return true;
395         }
396         return false;
397 }
398
399 static void update_proc_state(void)
400 {
401         DIR *dirp;
402         struct dirent *entry;
403         GArray *running_procs = NULL;
404         GHashTableIter iter;
405         int ret;
406         gpointer key, value;
407
408         running_procs = g_array_new(false, false, sizeof(pid_t));
409
410         if (!running_procs) {
411                 _E("fail to create garray for pids");
412                 return;
413         }
414
415         dirp = opendir("/proc");
416
417         if (dirp == NULL) {
418                 _E("/proc open is failed, and cannot updated running procs");
419                 return;
420         }
421
422         while ((entry = readdir(dirp)) != NULL) {
423                 pid_t pid;
424
425                 if (!isdigit(entry->d_name[0]))
426                         continue;
427                 pid = atoi(entry->d_name);
428                 g_array_append_val(running_procs, pid);
429         }
430
431         closedir(dirp);
432
433         g_hash_table_iter_init(&iter, logging_proc_list);
434
435         ret = pthread_mutex_lock(&proc_list_mutex);
436         if (ret) {
437                 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
438                 g_array_free(running_procs, true);
439                 return;
440         }
441
442         while (g_hash_table_iter_next(&iter, &key, &value)) {
443                 struct logging_infos *info = (struct logging_infos *)value;
444                 info->running = is_running_process(running_procs, info->pid);
445         }
446
447         g_array_free(running_procs, true);
448
449         ret = pthread_mutex_unlock(&proc_list_mutex);
450         if (ret) {
451                 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
452                 return;
453         }
454
455         need_to_update = false;
456         return;
457 }
458
459 static void update_proc_list(void)
460 {
461         GHashTableIter iter;
462         gpointer key, value;
463         time_t now;
464         struct logging_infos *infos;
465         int ret;
466
467         if (need_to_update)
468                 update_proc_state();
469
470         now = time(NULL);
471
472         g_hash_table_iter_init(&iter, logging_proc_list);
473
474         while (1) {
475                 ret = pthread_mutex_lock(&proc_list_mutex);
476                 if (ret) {
477                         _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
478                         return;
479                 }
480
481                 if (!g_hash_table_iter_next(&iter, &key, &value)) {
482                         ret = pthread_mutex_unlock(&proc_list_mutex);
483                         if (ret) {
484                                 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
485                                 return;
486                         }
487                         _D("finish proc list update");
488                         break;
489                 }
490                 infos = (struct logging_infos *)value;
491
492                 if (infos->running)
493                         update_logging_infos(infos, now, false);
494                 ret = pthread_mutex_unlock(&proc_list_mutex);
495                 if (ret) {
496                         _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
497                         return;
498                 }
499         }
500
501         return;
502 }
503
504 static void logging_update_state(void)
505 {
506         need_to_update = true;
507 }
508
509 static int check_running(gpointer key, gpointer value, gpointer user_data)
510 {
511         struct logging_infos *infos = (struct logging_infos *)value;
512
513         return !(infos->running);
514 }
515
516 static void reclaim_proc_list(void)
517 {
518         int ret;
519
520         ret = pthread_mutex_lock(&proc_list_mutex);
521         if (ret) {
522                 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
523                 return;
524         }
525
526         g_hash_table_foreach_remove(logging_proc_list, check_running, NULL);
527         ret = pthread_mutex_unlock(&proc_list_mutex);
528         if (ret) {
529                 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
530                 return;
531         }
532 }
533
534 static void logging(void)
535 {
536         update_proc_list();
537
538         if (g_hash_table_size(logging_proc_list) > MAX_PROC_LIST) {
539                 write_logging_infos(true);
540                 reclaim_proc_list();
541         } else
542                 write_logging_infos(false);
543 }
544
545 static void *logging_pthread(void *arg)
546 {
547         int ret = 0;
548
549         setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
550
551         while (1) {
552                 /*
553                  * When signalled by main thread,
554                  * it starts logging_pthread().
555                  */
556                 ret = pthread_mutex_lock(&logging_mutex);
557                 if ( ret ) {
558                         _E("logging thread::pthread_mutex_lock() failed, %d", ret);
559                         break;
560                 }
561
562                 ret = pthread_cond_wait(&logging_cond, &logging_mutex);
563                 if ( ret ) {
564                         _E("logging thread::pthread_cond_wait() failed, %d", ret);
565                         ret = pthread_mutex_unlock(&logging_mutex);
566                         if ( ret )
567                                 _E("logging thread::pthread_mutex_lock() failed, %d", ret);
568                         break;
569                 }
570
571                 logging();
572
573                 ret = pthread_mutex_unlock(&logging_mutex);
574                 if ( ret ) {
575                         _E("logging thread::pthread_mutex_unlock() failed, %d", ret);
576                         break;
577                 }
578         }
579
580         /* Now our thread finishes - cleanup TID */
581         logging_thread = 0;
582
583         return NULL;
584 }
585
586
587 static int logging_thread_create(void)
588 {
589         int ret = RESOURCED_ERROR_NONE;
590
591         if (logging_thread) {
592                 _I("logging thread %u already created", (unsigned)logging_thread);
593         } else {
594                 /* initialize logging_thread */
595                 ret = pthread_create(&logging_thread, NULL, (void *)logging_pthread, (void *)NULL);
596                 if (ret) {
597                         _E("pthread creation for logging_pthread failed, %d\n", ret);
598                         logging_thread = 0;
599                 } else {
600                         _D("pthread creation for logging success");
601                         pthread_detach(logging_thread);
602                 }
603         }
604
605         return ret;
606 }
607
608 static void free_key(gpointer key)
609 {
610         if (!key)
611                 free(key);
612 }
613
614 static void free_value(gpointer value)
615 {
616         int i;
617         struct logging_infos * info = (struct logging_infos *)value;
618
619         if (!info)
620                 return;
621
622         for (i = 0; i < num_log_infos; i++) {
623                 if (info->stats[i])
624                         free(info->stats[i]);
625         }
626
627         if (info->stats)
628                 free(info->stats);
629
630         free(info);
631 }
632
633 static void initialize_logging_proc_list(void)
634 {
635         DIR *dirp;
636         struct dirent *entry;
637         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
638         int cur_oom = -1;
639         FILE *fp = NULL;
640
641         dirp = opendir("/proc");
642
643         if (dirp == NULL) {
644                 _E("/proc open is failed, and cannot updated running procs");
645                 return;
646         }
647
648         while ((entry = readdir(dirp)) != NULL) {
649                 pid_t pid, pgid;
650
651                 if (!isdigit(entry->d_name[0]))
652                         continue;
653                 pid = atoi(entry->d_name);
654                 pgid = getpgid(pid);
655                 if (!pgid)
656                         continue;
657                 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
658                 fp = fopen(buf, "r+");
659                 if (fp == NULL)
660                         continue;
661                 if (fgets(buf, sizeof(buf), fp) == NULL) {
662                         fclose(fp);
663                         continue;
664                 }
665                 cur_oom = atoi(buf);
666                 fclose(fp);
667                 insert_proc_list(pid, pgid, cur_oom);
668         }
669
670         closedir(dirp);
671         write_logging_infos(true);
672         return;
673 }
674
675 static void logging_update_start(void)
676 {
677         int ret;
678         /* signal to logging_pthread to start */
679         ret = pthread_mutex_lock(&logging_mutex);
680         if (ret) {
681                 _E("logging_update_start::pthread_mutex_lock() failed, %d", ret);
682                 return;
683         }
684
685         ret = pthread_cond_signal(&logging_cond);
686         if (ret) {
687                 _E("logging_update_start::pthread_cond_wait() failed, %d", ret);
688                 ret = pthread_mutex_unlock(&logging_mutex);
689                 if ( ret )
690                         _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
691                 return;
692         }
693
694         _D("send signal logging_pthread");
695         ret = pthread_mutex_unlock(&logging_mutex);
696         if (ret) {
697                 _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
698                 return;
699         }
700 }
701
702 static void broadcast_logging_data_updated_signal(void)
703 {
704         int r;
705
706         r = broadcast_edbus_signal_str(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING,
707                         SIGNAL_LOGGING_UPDATED, NULL, NULL);
708         _I("broadcast logging_data updated signal!");
709
710         if (r < 0)
711                 _E("Failed: broadcast logging_data_updated signal");
712 }
713
714 static void logging_init_booting_done_edbus_signal_handler(void *data, DBusMessage *msg)
715 {
716         int ret;
717
718         ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
719                 SIGNAL_LOGGING_INIT);
720         if (ret == 0) {
721                 _D("there is booting done signal");
722                 return;
723         }
724         initialize_logging_proc_list();
725         _D("logging_init_booting_done_edbus_signal_handler");
726 }
727
728 static void logging_get_edbus_signal_handler(void *data, DBusMessage *msg)
729 {
730         int ret;
731
732         ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
733                 SIGNAL_LOGGING_GET);
734         if (ret == 0) {
735                 _D("there is logging get signal");
736                 return;
737         }
738         write_logging_infos(true);
739         _D("logging_get_edbus_signal_handler");
740         broadcast_logging_data_updated_signal();
741 }
742
743 static int logging_init(void)
744 {
745         int ret = RESOURCED_ERROR_NONE;
746
747         _D("logging_init start");
748
749         logging_proc_list = g_hash_table_new_full(
750                 g_str_hash,
751                 g_str_equal,
752                 free_key,
753                 free_value);
754
755         if (!logging_proc_list) {
756                 _E("fail g_hash_table_new_full() for logging_proc_list");
757                 return RESOURCED_ERROR_FAIL;
758         }
759
760         logging_ss_list = g_array_new(false, false,
761                 sizeof(struct logging_sub_sys));
762
763         if (logging_ss_list == NULL)
764                 return RESOURCED_ERROR_FAIL;
765
766         ret = logging_thread_create();
767         if (ret) {
768                 _E("logging thread create failed");
769                 return RESOURCED_ERROR_FAIL;
770         }
771
772         register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
773                 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_INIT,
774                     (void *)logging_init_booting_done_edbus_signal_handler);
775
776         register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
777                 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_GET,
778                     (void *)logging_get_edbus_signal_handler);
779
780         return RESOURCED_ERROR_NONE;
781 }
782
783 static int resourced_logging_control(void *data)
784 {
785         int ret = RESOURCED_ERROR_NONE;
786         struct logging_data_type *l_data;
787
788         if (!num_log_infos)
789                 return ret;
790
791         l_data = (struct logging_data_type *)data;
792
793         switch(l_data->control_type) {
794         case LOGGING_INSERT_PROC_LIST:
795                 if (l_data->args)
796                         insert_proc_list((pid_t)l_data->args[0],
797                                 (pid_t)l_data->args[1], (int)l_data->args[2]);
798                 break;
799         case LOGGING_UPDATE_PROC_INFO:
800                 logging_update_start();
801                 break;
802         case LOGGING_UPDATE_STATE:
803                 logging_update_state();
804                 break;
805         }
806         return ret;
807 }
808
809 static int resourced_logging_init(void *data)
810 {
811         logging_ops = &logging_modules_ops;
812
813         return logging_init();
814 }
815
816 static int resourced_logging_exit(void *data)
817 {
818         if (logging_ss_list)
819                 g_array_free(logging_ss_list, TRUE);
820         if (logging_proc_list)
821                 g_hash_table_destroy(logging_proc_list);
822         return RESOURCED_ERROR_NONE;
823 }
824
825 int logging_control(enum logging_control_type type, unsigned long *args)
826 {
827         struct logging_data_type l_data;
828
829         if (logging_ops) {
830                 l_data.control_type = type;
831                 l_data.args = args;
832                 return logging_ops->control(&l_data);
833         }
834
835         return RESOURCED_ERROR_NONE;
836 }
837
838 static const struct module_ops logging_modules_ops = {
839         .priority       = MODULE_PRIORITY_HIGH,
840         .name           = "logging",
841         .init           = resourced_logging_init,
842         .exit           = resourced_logging_exit,
843         .control        = resourced_logging_control,
844 };
845
846 MODULE_REGISTER(&logging_modules_ops)