svace fix
[platform/core/system/storaged.git] / src / storage / storage.c
1 /*
2  * storaged
3  *
4  * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
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 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <limits.h>
26 #include <vconf.h>
27 #include <sys/types.h>
28 #include <sys/statvfs.h>
29 #include <sys/stat.h>
30 #include <sys/shm.h>
31 #include <time.h>
32 #include <storage.h>
33 #include <tzplatform_config.h>
34 #include <glib.h>
35 #include <libgdbus/dbus-system.h>
36
37 #include "log.h"
38 #include "config-parser.h"
39 #include "module-intf.h"
40
41 #define MEMORY_STATUS_TMP_PATH  "/tmp"
42 #define MEMORY_STATUS_OPT_PATH  "/opt"
43 #define MEMNOTI_TMP_CRITICAL_VALUE (20)
44
45 #define MEMORY_MEGABYTE_VALUE   1048576
46
47 #define MEMNOTI_WARNING_VALUE  (5) /* 5% under */
48 #define MEMNOTI_CRITICAL_VALUE (0.1) /* 0.1% under */
49 #define MEMNOTI_FULL_VALUE     (0.0) /* 0.0% under */
50
51 #define SIGNAL_NEED_CLEANUP     "NeedCleanup"
52 #define MEMNOTI_TIMER_INTERVAL  5000 /* milliseconds */
53
54 #define SIGNAL_POWEROFF_STATE   "ChangeState"
55
56 #define STORAGE_CONF_FILE       "/etc/storaged/storage.conf"
57
58 #define STORAGED_DIR_PATH       "/run/storaged"
59 #define NEED_CLEANUP_DIR_PATH   "/run/storaged/needcleanup"
60 #define NEED_CLEANUP_FILE_PATH  "/run/storaged/needcleanup/trigger"
61
62 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
63
64 enum memnoti_level {
65         MEMNOTI_LEVEL_FULL = 0,
66         MEMNOTI_LEVEL_CRITICAL,
67         MEMNOTI_LEVEL_WARNING,
68         MEMNOTI_LEVEL_NORMAL,
69 };
70
71 enum memnoti_status {
72         MEMNOTI_DISABLE,
73         MEMNOTI_ENABLE,
74 };
75
76 enum memory_id {
77         MEMORY_INTERNAL = 0,
78         MEMORY_TMP,
79         MEMORY_OPT,
80 };
81
82 struct storage_config_info {
83         enum memory_id mem_id;
84         enum memnoti_level current_noti_level;
85         double warning_level;
86         double critical_level;
87         double full_level;
88 };
89
90 static guint memnoti_timer;
91
92 static guint id_booting_done;
93 static guint id_storage_poweroff;
94
95 static struct storage_config_info storage_internal_info = {
96         .mem_id             = MEMORY_INTERNAL,
97         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
98         .warning_level      = MEMNOTI_WARNING_VALUE,
99         .critical_level     = MEMNOTI_CRITICAL_VALUE,
100         .full_level         = MEMNOTI_FULL_VALUE,
101 };
102
103 static struct storage_config_info storage_tmp_info = {
104         .mem_id             = MEMORY_TMP,
105         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
106         .warning_level      = MEMNOTI_TMP_CRITICAL_VALUE,
107         .critical_level     = MEMNOTI_TMP_CRITICAL_VALUE,
108         .full_level         = MEMNOTI_FULL_VALUE,
109 };
110
111 static struct storage_config_info storage_opt_info = {
112         .mem_id             = MEMORY_OPT,
113         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
114         .warning_level      = MEMNOTI_WARNING_VALUE,
115         .critical_level     = MEMNOTI_CRITICAL_VALUE,
116         .full_level         = MEMNOTI_FULL_VALUE,
117 };
118
119 static void write_file()
120 {
121         FILE *fp;
122
123         fp = fopen(NEED_CLEANUP_FILE_PATH, "w+");
124         if (fp) {
125                 fprintf(fp, "needcleanup\n");
126                 fclose(fp);
127         } else
128                 _E("Fail to open %s", NEED_CLEANUP_FILE_PATH);
129 }
130
131 static void memcleanup_send_broadcast(struct storage_config_info *info, enum memnoti_level level, enum memnoti_level prev_level)
132 {
133         time_t t;
134         struct tm timeinfo;
135         enum tzplatform_variable path;
136         char *value;
137         char buf[20];
138
139         if (info->mem_id == MEMORY_INTERNAL)
140                 path = TZ_SYS_USER;
141         else if (info->mem_id == MEMORY_TMP)
142                 path = TZ_SYS_TMP;
143         else if (info->mem_id == MEMORY_OPT)
144                 path = TZ_SYS_OPT;
145         else
146                 return;
147
148         if (prev_level <= level)
149                 return;
150
151         if (level == MEMNOTI_LEVEL_WARNING)
152                 value = "Warning";
153         else if (level == MEMNOTI_LEVEL_CRITICAL)
154                 value = "Critical";
155         else if (level == MEMNOTI_LEVEL_FULL)
156                 value = "Full";
157         else
158                 return;
159
160         write_file();
161
162         t = time(NULL);
163         if (localtime_r(&t, &timeinfo) == NULL) {
164                 _E("Failed to localtime_r");
165                 goto out;
166         }
167
168         strftime(buf, sizeof(buf), "%b %d %T", &timeinfo);
169         _D("time: %s path: %d level: %s", buf, path, value);
170
171 out:
172         dbus_handle_broadcast_dbus_signal_var(STORAGED_PATH_LOWMEM, STORAGED_INTERFACE_LOWMEM,
173                         SIGNAL_NEED_CLEANUP, g_variant_new("(is)", path, value));
174
175 }
176
177 static void __cb(GVariant *var, void *user_data, GError *err)
178 {
179          int ret;
180
181         if (!var) {
182                 _E("no message [%s]", err->message);
183                 return;
184         }
185
186         if (!dh_get_param_from_var(var, "(i)", &ret)) {
187                 _E("no message [%s]", g_variant_get_type_string(var));
188                 goto out;
189         }
190
191         _D("reply value : %d", ret);
192
193 out:
194         g_variant_unref(var);
195 }
196
197 static int launch_memory_popup(int num, ...)
198 {
199         int ret;
200         va_list args;
201
202         va_start(args, num);
203
204         ret = dbus_handle_method_async_pairs_with_reply(POPUP_BUS_NAME,
205                         POPUP_PATH_SYSTEM,
206                         POPUP_INTERFACE_SYSTEM,
207                         "PopupLaunch",
208                         num,
209                         args,
210                         __cb,
211                         -1,
212                         NULL);
213
214         va_end(args);
215
216         return ret;
217 }
218
219 static int memnoti_popup(enum memnoti_level level)
220 {
221         int ret = -1;
222         int val = -1;
223         char *value = NULL;
224
225         if (level != MEMNOTI_LEVEL_WARNING && level != MEMNOTI_LEVEL_CRITICAL) {
226                 _E("level check error : %d", level);
227                 return 0;
228         }
229
230         if (level == MEMNOTI_LEVEL_WARNING)
231                 value = "lowstorage_warning";
232         else if (level == MEMNOTI_LEVEL_CRITICAL)
233                 value = "lowstorage_critical";
234
235         ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
236         if (val == 0 || ret != 0)
237                 return -1;
238
239         if (!value)
240                 return 0;
241
242         return launch_memory_popup(2, "_SYSPOPUP_CONTENT_", value);
243 }
244
245 static void storage_status_broadcast(struct storage_config_info *info, double total, double avail)
246 {
247         double level = (avail/total)*100;
248         enum memnoti_level prev_noti_level;
249
250         prev_noti_level = info->current_noti_level;
251         if (level <= info->full_level) {
252                 if (info->current_noti_level == MEMNOTI_LEVEL_FULL)
253                         return;
254                 info->current_noti_level = MEMNOTI_LEVEL_FULL;
255
256                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
257
258                 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
259                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
260                 return;
261         }
262
263         if (level <= info->critical_level) {
264                 if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL)
265                         return;
266                 info->current_noti_level = MEMNOTI_LEVEL_CRITICAL;
267
268                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
269
270                 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
271                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
272                 return;
273         }
274
275         if (level <= info->warning_level) {
276                 info->current_noti_level = MEMNOTI_LEVEL_WARNING;
277                 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
278                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
279
280                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
281         } else
282                 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
283
284 }
285
286 static int storage_get_memory_size(const char *path, struct statvfs *s)
287 {
288         int ret;
289
290         if (!path) {
291                 _E("input param error");
292                 return -EINVAL;
293         }
294
295         ret = statvfs(path, s);
296         if (ret) {
297                 _E("fail to get storage size");
298                 return -errno;
299         }
300
301         return 0;
302 }
303
304 static void get_storage_status(const char *path, struct statvfs *s)
305 {
306         if (strcmp(path, tzplatform_getenv(TZ_SYS_USER)) == 0)
307                 storage_get_internal_memory_size(s);
308         else
309                 storage_get_memory_size(path, s);
310 }
311
312 static void init_storage_config_info(const char *path, struct storage_config_info *info)
313 {
314         struct statvfs s;
315         double dAvail = 0.0;
316         double dTotal = 0.0;
317
318         get_storage_status(path, &s);
319
320         dTotal = (double)s.f_frsize * s.f_blocks;
321         dAvail = (double)s.f_bsize * s.f_bavail;
322
323         info->full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
324
325         _I("%s t: %4.0lf a: %4.0lf(%4.2lf) w:%4.4lf c:%4.4lf f:%4.4lf",
326                 path, dTotal, dAvail, (dAvail*100/dTotal), info->warning_level, info->critical_level, info->full_level);
327 }
328
329 static void check_internal_storage_popup(struct storage_config_info *info)
330 {
331         static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
332         int ret;
333
334         if (info->current_noti_level < MEMNOTI_LEVEL_NORMAL && info->current_noti_level < old) {
335                 ret = memnoti_popup(info->current_noti_level);
336                 if (ret != 0)
337                         info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
338         }
339         old = info->current_noti_level;
340 }
341
342 static gboolean check_storage_status(gpointer data)
343 {
344         struct statvfs s;
345         double dAvail = 0.0;
346         double dTotal = 0.0;
347
348         /* check internal */
349         storage_get_internal_memory_size(&s);
350         dTotal = (double)s.f_frsize * s.f_blocks;
351         dAvail = (double)s.f_bsize * s.f_bavail;
352         storage_status_broadcast(&storage_internal_info, dTotal, dAvail);
353         check_internal_storage_popup(&storage_internal_info);
354         /* check tmp */
355         storage_get_memory_size(MEMORY_STATUS_TMP_PATH, &s);
356         dTotal = (double)s.f_frsize * s.f_blocks;
357         dAvail = (double)s.f_bsize * s.f_bavail;
358         storage_status_broadcast(&storage_tmp_info, dTotal, dAvail);
359         /* check opt */
360         storage_get_memory_size(MEMORY_STATUS_OPT_PATH, &s);
361         dTotal = (double)s.f_frsize * s.f_blocks;
362         dAvail = (double)s.f_bsize * s.f_bavail;
363         storage_status_broadcast(&storage_opt_info, dTotal, dAvail);
364
365         if (memnoti_timer) {
366                 g_source_remove(memnoti_timer);
367                 memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
368                                 check_storage_status, NULL);
369         }
370
371         return G_SOURCE_REMOVE;
372 }
373
374 static int init_storage_config_info_all(void)
375 {
376         init_storage_config_info(tzplatform_getenv(TZ_SYS_USER), &storage_internal_info);
377         init_storage_config_info(MEMORY_STATUS_TMP_PATH, &storage_tmp_info);
378         init_storage_config_info(MEMORY_STATUS_OPT_PATH, &storage_opt_info);
379         memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
380                                 check_storage_status, NULL);
381         if (memnoti_timer == 0)
382                 _E("fail mem available noti timer add");
383         return 0;
384 }
385
386 static GVariant *dbus_getstatus(GDBusConnection *conn,
387                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
388                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
389 {
390         struct statvfs s;
391         unsigned long long dAvail = 0.0;
392         unsigned long long dTotal = 0.0;
393
394         storage_get_internal_memory_size(&s);
395         dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
396         dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
397
398         return g_variant_new("(tt)", dTotal, dAvail);
399 }
400
401 static GVariant *dbus_get_storage_status(GDBusConnection *conn,
402                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
403                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
404 {
405         const char *temp;
406         char *str_path;
407         struct statvfs s;
408         struct stat buf;
409         pid_t pid;
410         unsigned long long dAvail = 0.0;
411         unsigned long long dTotal = 0.0;
412         int ret;
413         bool success = true;
414
415         g_variant_get(param, "(s)", &str_path);
416
417         temp = tzplatform_getenv(TZ_SYS_USER);
418         if (!strncmp(str_path, temp, strlen(temp))) {
419                 ret = stat(str_path, &buf);
420                 if (ret == 0) {
421                         ret = storage_get_internal_memory_size(&s);
422                         if (ret < 0)
423                                 success = false;
424                 } else
425                         success = false;
426         } else {
427                 ret = storage_get_memory_size(str_path, &s);
428                 if (ret < 0)
429                         success = false;
430         }
431
432         if (success) {
433                 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
434                 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
435         }
436
437         pid = dbus_handle_get_sender_pid(NULL, sender);
438
439         _D("[request %d] path %s total %4.0llu avail %4.0llu", pid, str_path, dTotal, dAvail);
440
441         g_free(str_path);
442         return g_variant_new("(tt)", dTotal, dAvail);
443 }
444
445 static GVariant *dbus_getstatvfs(GDBusConnection *conn,
446                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
447                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
448 {
449         char *str_path;
450         pid_t pid;
451         struct statvfs s;
452
453         g_variant_get(param, "(s)", &str_path);
454
455         storage_get_memory_size(str_path, &s);
456
457         pid = dbus_handle_get_sender_pid(NULL, sender);
458
459         _D("[request %d] path %s", pid, str_path);
460
461         g_free(str_path);
462
463         return g_variant_new("(ttttttttttt)", (guint64)(s.f_bsize), (guint64)(s.f_frsize),
464                         (guint64)(s.f_blocks), (guint64)(s.f_bfree), (guint64)(s.f_bavail),
465                         (guint64)(s.f_files), (guint64)(s.f_ffree), (guint64)(s.f_favail),
466                         (guint64)(s.f_fsid), (guint64)(s.f_flag), (guint64)(s.f_namemax));
467
468 }
469
470 static GVariant *dbus_get_storage_level(GDBusConnection *conn,
471                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
472                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
473 {
474         int path_id;
475         enum memnoti_level level;
476         char *value;
477
478         g_variant_get(param, "(i)", &path_id);
479
480         if (path_id == TZ_SYS_USER)
481                 level = storage_internal_info.current_noti_level;
482         else if (path_id == TZ_SYS_OPT)
483                 level = storage_opt_info.current_noti_level;
484         else if (path_id == TZ_SYS_TMP)
485                 level = storage_tmp_info.current_noti_level;
486         else
487                 level = -1;
488
489         if (level == MEMNOTI_LEVEL_WARNING)
490                 value = "Warning";
491         else if (level == MEMNOTI_LEVEL_CRITICAL)
492                 value = "Critical";
493         else if (level == MEMNOTI_LEVEL_FULL)
494                 value = "Full";
495         else if (level == MEMNOTI_LEVEL_NORMAL)
496                 value = "Normal";
497         else
498                 value = "Not supported path";
499
500         return g_variant_new("(s)", value);
501 }
502
503 static const dbus_method_s storage_methods[] = {
504         { "getstorage",     NULL,          "tt", dbus_getstatus },
505         { "GetStatus",       "s",          "tt", dbus_get_storage_status },
506         { "GetStatvfs",      "s", "ttttttttttt", dbus_getstatvfs },
507         { "GetStorageLevel", "i",           "s", dbus_get_storage_level },
508         /* Add methods here */
509 };
510
511 static dbus_interface_u storage_interface = {
512         .name = STORAGED_INTERFACE_STORAGE,
513         .methods = storage_methods,
514         .nr_methods = ARRAY_SIZE(storage_methods),
515 };
516
517 static void booting_done(GDBusConnection  *conn,
518                 const gchar *sender,
519                 const gchar *path,
520                 const gchar *iface,
521                 const gchar *name,
522                 GVariant *param,
523                 gpointer data)
524 {
525         static int done;
526
527         if (done > 0)
528                 return;
529         done = 1;
530         _I("booting done");
531
532         if (init_storage_config_info_all() == -1)
533                 _E("fail remain mem noti control fd init");
534 }
535
536 static void storage_poweroff(GDBusConnection  *conn,
537                 const gchar *sender,
538                 const gchar *path,
539                 const gchar *iface,
540                 const gchar *name,
541                 GVariant *param,
542                 gpointer data)
543 {
544         if (memnoti_timer) {
545                 g_source_remove(memnoti_timer);
546                 memnoti_timer = 0;
547         }
548 }
549
550 static int load_config(struct parse_result *result, void *user_data)
551 {
552         struct storage_config_info *info = (struct storage_config_info *)user_data;
553         char *name;
554         char *value;
555
556         if (!info)
557                 return -EINVAL;
558
559         if (!MATCH(result->section, "LOWSTORAGE"))
560                 return -EINVAL;
561
562         _D("%s,%s,%s", result->section, result->name, result->value);
563
564         name = result->name;
565         value = result->value;
566
567         if (MATCH(name, "WARNING_LEVEL"))
568                 info->warning_level = (double)atof(value);
569         else if (MATCH(name, "CRITICAL_LEVEL"))
570                 info->critical_level = (double)atof(value);
571         else if (MATCH(name, "FULL_LEVEL"))
572                 info->full_level = (double)atof(value);
573
574         return 0;
575 }
576
577 static void storage_config_load(struct storage_config_info *info)
578 {
579         int ret;
580
581         ret = config_parse(STORAGE_CONF_FILE, load_config, info);
582         if (ret < 0)
583                 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret);
584 }
585
586 static void storage_init(void *data)
587 {
588         struct stat buf;
589         int ret;
590
591         storage_config_load(&storage_internal_info);
592
593         /* System Session is loaded completely */
594         id_booting_done = subscribe_dbus_signal(NULL, SYSTEMD_DBUS_PATH,
595                         SYSTEMD_DBUS_IFACE_MANAGER,
596                         SYSTEMD_DBUS_SIGNAL_SYSTEM_STARTUP_FINISHED,
597                         booting_done, NULL, NULL);
598
599         id_storage_poweroff = subscribe_dbus_signal(NULL, DEVICED_PATH_POWEROFF,
600                         DEVICED_INTERFACE_POWEROFF,
601                         SIGNAL_POWEROFF_STATE,
602                         storage_poweroff, NULL, NULL);
603
604         ret = dbus_handle_register_dbus_object(NULL, STORAGED_PATH_STORAGE,
605                         &storage_interface);
606         if (ret < 0)
607                 _E("Failed to register dbus interface and methods(%d)", ret);
608
609         ret = stat(STORAGED_DIR_PATH, &buf);
610         if (ret < 0) {
611                 ret = mkdir(STORAGED_DIR_PATH, 0644);
612                 if (ret < 0)
613                         _E("Failed to make directory: %d", errno);
614         } else if (!S_ISDIR(buf.st_mode)) {
615                 ret = remove(STORAGED_DIR_PATH);
616                 if (ret < 0)
617                         _E("Fail to remove %s. errno: %d", STORAGED_DIR_PATH, errno);
618                 ret = mkdir(STORAGED_DIR_PATH, 0644);
619                 if (ret < 0)
620                         _E("Failed to make directory: %d", errno);
621         } else {
622                 ret = chmod(STORAGED_DIR_PATH, 0644);
623                 if (ret < 0)
624                         _E("Fail to change permissions of a file");
625         }
626
627         ret = stat(NEED_CLEANUP_DIR_PATH, &buf);
628         if (ret < 0) {
629                 ret = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
630                 if (ret < 0)
631                         _E("Failed to make directory: %d", errno);
632         } else if (!S_ISDIR(buf.st_mode)) {
633                 ret = remove(NEED_CLEANUP_DIR_PATH);
634                 if (ret < 0)
635                         _E("Fail to remove %s. errno: %d", NEED_CLEANUP_DIR_PATH, errno);
636                 ret = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
637                 if (ret < 0)
638                         _E("Failed to make directory: %d", errno);
639         } else {
640                 ret = chmod(NEED_CLEANUP_DIR_PATH, 0644);
641                 if (ret < 0)
642                         _E("Fail to change permissions of a file");
643         }
644 }
645
646 static void storage_exit(void *data)
647 {
648         /* unregister notifier for below each event */
649         unsubscribe_dbus_signal(NULL, id_booting_done);
650         unsubscribe_dbus_signal(NULL, id_storage_poweroff);
651 }
652
653 static storaged_module_interface storage_module = {
654         .name = "storage",
655         .init = storage_init,
656         .exit = storage_exit,
657 };
658
659 __attribute__ ((visibility("default")))storaged_module_interface *
660 storaged_get_module_interface(void)
661 {
662         return &storage_module;
663 }