improve line coverage
[platform/core/pim/contacts-service.git] / common / ctsvc_inotify.c
1 /*
2  * Contacts Service
3  *
4  * Copyright (c) 2010 - 2015 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 #include <errno.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <sys/inotify.h>
25
26 #include "contacts.h"
27 #include "ctsvc_internal.h"
28 #include "ctsvc_notify.h"
29 #include "ctsvc_view.h"
30 #include "ctsvc_handle.h"
31 #include "ctsvc_inotify.h"
32
33 #include <stdbool.h>
34
35 #ifdef _CONTACTS_IPC_CLIENT
36 #include "ctsvc_client_ipc.h"
37 #include "ctsvc_client_utils.h"
38 #endif
39
40 typedef struct {
41         int wd;
42         char *view_uri;
43         contacts_db_changed_cb cb;
44         void *cb_data;
45         bool blocked;
46 } noti_info;
47
48 struct socket_init_noti_info {
49         int wd;
50         int subscribe_count;
51         void (*cb)(void *);
52         void *cb_data;
53 };
54
55 static GHashTable *_ctsvc_socket_init_noti_table = NULL;
56 static int __ctsvc_inoti_ref = 0;
57 static int __inoti_fd = -1;
58 static guint __inoti_handler = 0;
59 static GSList *__noti_list = NULL;
60
61 static inline void __ctsvc_inotify_handle_callback(GSList *noti_list, int wd, uint32_t mask)
62 {
63         noti_info *noti;
64         GSList *it = NULL;
65
66         for (it = noti_list; it; it = it->next) {
67                 noti = (noti_info *)it->data;
68
69                 if (noti->wd == wd) {
70
71 #ifdef _CONTACTS_IPC_CLIENT
72                         if (ctsvc_ipc_is_busy()) {
73                                 /* hold the line */
74                                 noti->blocked = true;
75                                 continue;
76                         }
77 #endif
78                         if ((mask & IN_CLOSE_WRITE) && noti->cb) {
79                                 DBG("%s", noti->view_uri);
80                                 noti->cb(noti->view_uri, noti->cb_data);
81                         }
82                 }
83         }
84 }
85
86 static void _ctsvc_inotify_socket_init_noti_table_foreach_cb(gpointer key, gpointer value, gpointer user_data)
87 {
88         struct socket_init_noti_info *noti_info = value;
89
90         int wd = GPOINTER_TO_INT(user_data);
91         if (noti_info->wd == wd)
92                 noti_info->cb(noti_info->cb_data);
93 }
94
95 static gboolean __ctsvc_inotify_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
96 {
97         int fd, ret;
98         struct inotify_event ie;
99         char name[FILENAME_MAX] = {0};
100
101         fd = g_io_channel_unix_get_fd(src);
102
103         while (0 < (ret = read(fd, &ie, sizeof(ie)))) {
104                 if (sizeof(ie) == ret) {
105                         if (_ctsvc_socket_init_noti_table)
106                                 g_hash_table_foreach(_ctsvc_socket_init_noti_table, _ctsvc_inotify_socket_init_noti_table_foreach_cb, GINT_TO_POINTER(ie.wd));
107
108                         if (__noti_list)
109                                 __ctsvc_inotify_handle_callback(__noti_list, ie.wd, ie.mask);
110
111                         while (0 != ie.len) {
112                                 ret = read(fd, name, (ie.len < sizeof(name)) ? ie.len : sizeof(name));
113                                 if (-1 == ret) {
114                                         if (EINTR == errno)
115                                                 continue;
116                                         else
117                                                 return TRUE;
118                                 }
119                                 if (ie.len < ret)
120                                         ie.len = 0;
121                                 else
122                                         ie.len -= ret;
123                         }
124                 } else {
125                         while (ret < sizeof(ie)) {
126                                 int read_size;
127                                 read_size = read(fd, name, sizeof(ie)-ret);
128                                 if (-1 == read_size) {
129                                         if (EINTR == errno)
130                                                 continue;
131                                         else
132                                                 return TRUE;
133                                 }
134                                 ret += read_size;
135                         }
136                 }
137         }
138
139         return TRUE;
140 }
141
142 static inline int __ctsvc_inotify_attach_handler(int fd)
143 {
144         guint ret;
145         GIOChannel *channel;
146
147         RETVM_IF(fd < 0, CONTACTS_ERROR_INVALID_PARAMETER, "fd is invalid");
148
149         channel = g_io_channel_unix_new(fd);
150         RETVM_IF(NULL == channel, CONTACTS_ERROR_SYSTEM, "System: g_io_channel_unix_new() Fail");
151
152         g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
153
154         ret = g_io_add_watch(channel, G_IO_IN, __ctsvc_inotify_gio_cb, NULL);
155         g_io_channel_unref(channel);
156
157         return ret;
158 }
159
160 int ctsvc_inotify_init(void)
161 {
162         int ret;
163
164         if (0 < __ctsvc_inoti_ref) {
165                 __ctsvc_inoti_ref++;
166                 return CONTACTS_ERROR_NONE;
167         }
168         __inoti_fd = inotify_init();
169         RETVM_IF(-1 == __inoti_fd, CONTACTS_ERROR_SYSTEM,
170                         "System: inotify_init() Fail(%d)", errno);
171
172         ret = fcntl(__inoti_fd, F_SETFD, FD_CLOEXEC);
173         WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
174         ret = fcntl(__inoti_fd, F_SETFL, O_NONBLOCK);
175         WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
176
177         __inoti_handler = __ctsvc_inotify_attach_handler(__inoti_fd);
178         if (__inoti_handler <= 0) {
179                 /* LCOV_EXCL_START */
180                 ERR("__ctsvc_inotify_attach_handler() Fail");
181                 close(__inoti_fd);
182                 __inoti_fd = -1;
183                 __inoti_handler = 0;
184                 return CONTACTS_ERROR_SYSTEM;
185                 /* LCOV_EXCL_STOP */
186         }
187
188         __ctsvc_inoti_ref++;
189         return CONTACTS_ERROR_NONE;
190 }
191
192 static inline int __ctsvc_inotify_get_wd(int fd, const char *notipath)
193 {
194         return inotify_add_watch(fd, notipath, IN_ACCESS);
195 }
196
197 static inline int __ctsvc_inotify_watch(int fd, const char *notipath)
198 {
199         int ret;
200
201         ret = inotify_add_watch(fd, notipath, IN_CLOSE_WRITE);
202         RETVM_IF(-1 == ret, CONTACTS_ERROR_SYSTEM,
203                         "System: inotify_add_watch() Fail(%d)", errno);
204
205         return CONTACTS_ERROR_NONE;
206 }
207
208 static int __ctsvc_noti_get_file_path(const char *view_uri, char **path)
209 {
210         ctsvc_record_type_e match = ctsvc_view_get_record_type(view_uri);
211         char *file = NULL;
212
213         switch ((int)match) {
214         case CTSVC_RECORD_ADDRESSBOOK:
215                 file = CTSVC_NOTI_ADDRESSBOOK_CHANGED;
216                 break;
217         case CTSVC_RECORD_GROUP:
218                 file = CTSVC_NOTI_GROUP_CHANGED;
219                 break;
220         case CTSVC_RECORD_PERSON:
221                 file = CTSVC_NOTI_PERSON_CHANGED;
222                 break;
223         case CTSVC_RECORD_CONTACT:
224         case CTSVC_RECORD_SIMPLE_CONTACT:
225                 file = CTSVC_NOTI_CONTACT_CHANGED;
226                 break;
227         case CTSVC_RECORD_MY_PROFILE:
228                 file = CTSVC_NOTI_MY_PROFILE_CHANGED;
229                 break;
230         case CTSVC_RECORD_NAME:
231                 file = CTSVC_NOTI_NAME_CHANGED;
232                 break;
233         case CTSVC_RECORD_COMPANY:
234                 file = CTSVC_NOTI_COMPANY_CHANGED;
235                 break;
236         case CTSVC_RECORD_NOTE:
237                 file = CTSVC_NOTI_NOTE_CHANGED;
238                 break;
239         case CTSVC_RECORD_NUMBER:
240                 file = CTSVC_NOTI_NUMBER_CHANGED;
241                 break;
242         case CTSVC_RECORD_EMAIL:
243                 file = CTSVC_NOTI_EMAIL_CHANGED;
244                 break;
245         case CTSVC_RECORD_URL:
246                 file = CTSVC_NOTI_URL_CHANGED;
247                 break;
248         case CTSVC_RECORD_EVENT:
249                 file = CTSVC_NOTI_EVENT_CHANGED;
250                 break;
251         case CTSVC_RECORD_NICKNAME:
252                 file = CTSVC_NOTI_NICKNAME_CHANGED;
253                 break;
254         case CTSVC_RECORD_ADDRESS:
255                 file = CTSVC_NOTI_ADDRESS_CHANGED;
256                 break;
257         case CTSVC_RECORD_MESSENGER:
258                 file = CTSVC_NOTI_MESSENGER_CHANGED;
259                 break;
260         case CTSVC_RECORD_GROUP_RELATION:
261                 file = CTSVC_NOTI_GROUP_RELATION_CHANGED;
262                 break;
263         case CTSVC_RECORD_ACTIVITY:
264                 file = CTSVC_NOTI_ACTIVITY_CHANGED;
265                 break;
266         case CTSVC_RECORD_ACTIVITY_PHOTO:
267                 file = CTSVC_NOTI_ACTIVITY_PHOTO_CHANGED;
268                 break;
269         case CTSVC_RECORD_PROFILE:
270                 file = CTSVC_NOTI_PROFILE_CHANGED;
271                 break;
272         case CTSVC_RECORD_RELATIONSHIP:
273                 file = CTSVC_NOTI_RELATIONSHIP_CHANGED;
274                 break;
275         case CTSVC_RECORD_IMAGE:
276                 file = CTSVC_NOTI_IMAGE_CHANGED;
277                 break;
278         case CTSVC_RECORD_EXTENSION:
279                 file = CTSVC_NOTI_DATA_CHANGED;
280                 break;
281         case CTSVC_RECORD_PHONELOG:
282                 file = CTSVC_NOTI_PHONELOG_CHANGED;
283                 break;
284         case CTSVC_RECORD_SPEEDDIAL:
285                 file = CTSVC_NOTI_SPEEDDIAL_CHANGED;
286                 break;
287         case CTSVC_RECORD_SDN:
288                 file = CTSVC_NOTI_SDN_CHANGED;
289                 break;
290         case CTSVC_RECORD_SIP:
291                 file = CTSVC_NOTI_SIP_CHANGED;
292                 break;
293         case CTSVC_RECORD_RESULT:
294         default:
295                 /* LCOV_EXCL_START */
296                 ERR("The type(%s) is not supported", view_uri);
297                 return CONTACTS_ERROR_INVALID_PARAMETER;
298                 /* LCOV_EXCL_STOP */
299         }
300
301         *path = ctsvc_inotify_makepath(file);
302         if (NULL == *path) {
303                 /* LCOV_EXCL_START */
304                 ERR("ctsvc_inotify_makepath() fail");
305                 return CONTACTS_ERROR_SYSTEM;
306                 /* LCOV_EXCL_STOP */
307         }
308
309         return CONTACTS_ERROR_NONE;
310 }
311
312 int ctsvc_inotify_subscribe_ipc_ready(void (*cb)(void *), void *user_data)
313 {
314         char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
315         struct socket_init_noti_info *noti_info = NULL;
316
317         if (NULL == noti_path) {
318                 /* LCOV_EXCL_START */
319                 ERR("ctsvc_inotify_makepath() fail");
320                 return CONTACTS_ERROR_SYSTEM;
321                 /* LCOV_EXCL_STOP */
322         }
323
324         if (NULL == _ctsvc_socket_init_noti_table)
325                 _ctsvc_socket_init_noti_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
326         else
327                 noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
328
329         if (NULL == noti_info) {
330                 int wd = __ctsvc_inotify_get_wd(__inoti_fd, noti_path);
331                 if (-1 == wd) {
332                         /* LCOV_EXCL_START */
333                         ERR("__ctsvc_inotify_get_wd() Fail(noti_path=%s, errno : %d)", noti_path, errno);
334                         free(noti_path);
335                         if (errno == EACCES)
336                                 return CONTACTS_ERROR_PERMISSION_DENIED;
337                         return CONTACTS_ERROR_SYSTEM;
338                         /* LCOV_EXCL_STOP */
339                 }
340
341                 int ret = __ctsvc_inotify_watch(__inoti_fd, noti_path);
342                 if (CONTACTS_ERROR_NONE != ret) {
343                         /* LCOV_EXCL_START */
344                         ERR("__ctsvc_inotify_watch() Fail");
345                         free(noti_path);
346                         return ret;
347                         /* LCOV_EXCL_STOP */
348                 }
349
350                 noti_info = calloc(1, sizeof(struct socket_init_noti_info));
351                 if (NULL == noti_info) {
352                         /* LCOV_EXCL_START */
353                         ERR("calloc() return NULL");
354                         free(noti_path);
355                         return ret;
356                         /* LCOV_EXCL_STOP */
357                 }
358
359                 noti_info->wd = wd;
360                 noti_info->cb = cb;
361                 noti_info->cb_data = user_data;
362                 g_hash_table_insert(_ctsvc_socket_init_noti_table, noti_path, noti_info);
363         } else {
364                 free(noti_path);
365         }
366         noti_info->subscribe_count++;
367         return CONTACTS_ERROR_NONE;
368 }
369
370 int ctsvc_inotify_unsubscribe_ipc_ready()
371 {
372         RETV_IF(NULL == _ctsvc_socket_init_noti_table, CONTACTS_ERROR_INVALID_PARAMETER);
373
374         char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
375         struct socket_init_noti_info *noti_info = NULL;
376
377         if (NULL == noti_path) {
378                 /* LCOV_EXCL_START */
379                 ERR("ctsvc_inotify_makepath() fail");
380                 return CONTACTS_ERROR_SYSTEM;
381                 /* LCOV_EXCL_STOP */
382         }
383
384         noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
385         if (NULL == noti_info) {
386                 /* LCOV_EXCL_START */
387                 ERR("g_hash_table_lookup() return NULL");
388                 free(noti_path);
389                 return CONTACTS_ERROR_INVALID_PARAMETER;
390                 /* LCOV_EXCL_STOP */
391         }
392
393         if (1 == noti_info->subscribe_count) {
394                 int wd = noti_info->wd;
395                 inotify_rm_watch(__inoti_fd, wd);
396                 /* free noti_info automatically */
397                 g_hash_table_remove(_ctsvc_socket_init_noti_table, noti_path);
398         } else {
399                 noti_info->subscribe_count--;
400         }
401
402         free(noti_path);
403         return CONTACTS_ERROR_NONE;
404 }
405
406
407 int ctsvc_inotify_subscribe(const char *view_uri, contacts_db_changed_cb cb, void *data)
408 {
409         int ret, wd;
410         noti_info *noti, *same_noti = NULL;
411         GSList *it;
412         char *path = NULL;
413
414         RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
415         RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
416                         "__inoti_fd(%d) is invalid", __inoti_fd);
417
418         ret = __ctsvc_noti_get_file_path(view_uri, &path);
419         if (CONTACTS_ERROR_NONE != ret) {
420                 /* LCOV_EXCL_START */
421                 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
422                 return ret;
423                 /* LCOV_EXCL_STOP */
424         }
425
426         wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
427         if (-1 == wd) {
428                 /* LCOV_EXCL_START */
429                 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
430                 free(path);
431                 if (errno == EACCES)
432                         return CONTACTS_ERROR_PERMISSION_DENIED;
433                 return CONTACTS_ERROR_SYSTEM;
434                 /* LCOV_EXCL_STOP */
435         }
436
437         for (it = __noti_list; it; it = it->next) {
438                 if (it->data) {
439                         same_noti = it->data;
440                         if (same_noti->wd == wd && same_noti->cb == cb
441                                         && STRING_EQUAL == strcmp(same_noti->view_uri, view_uri)
442                                         && same_noti->cb_data == data) {
443                                 break;
444                         } else {
445                                 same_noti = NULL;
446                         }
447                 }
448         }
449
450         if (same_noti) {
451                 __ctsvc_inotify_watch(__inoti_fd, path);
452                 /* LCOV_EXCL_START */
453                 ERR("The same callback(%s) is already exist", view_uri);
454                 free(path);
455                 return CONTACTS_ERROR_INVALID_PARAMETER;
456                 /* LCOV_EXCL_STOP */
457         }
458
459         ret = __ctsvc_inotify_watch(__inoti_fd, path);
460         free(path);
461         RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "__ctsvc_inotify_watch() Fail");
462
463         noti = calloc(1, sizeof(noti_info));
464         RETVM_IF(NULL == noti, CONTACTS_ERROR_OUT_OF_MEMORY, "calloc() Fail");
465
466         noti->wd = wd;
467         noti->view_uri = strdup(view_uri);
468         noti->cb_data = data;
469         noti->cb = cb;
470         noti->blocked = false;
471
472         __noti_list = g_slist_append(__noti_list, noti);
473
474         return CONTACTS_ERROR_NONE;
475 }
476
477 static inline int __ctsvc_del_noti(GSList **noti_list, int wd, const char *view_uri, void *cb, void *user_data)
478 {
479         int del_cnt, remain_cnt;
480         GSList *it, *result;
481
482         del_cnt = 0;
483         remain_cnt = 0;
484
485         it = result = *noti_list;
486         while (it) {
487                 noti_info *noti = it->data;
488                 if (noti && wd == noti->wd) {
489                         if (cb == noti->cb && user_data == noti->cb_data
490                                         && STRING_EQUAL == strcmp(noti->view_uri, view_uri)) {
491                                 it = it->next;
492                                 result = g_slist_remove(result, noti);
493                                 free(noti->view_uri);
494                                 free(noti);
495                                 del_cnt++;
496                                 continue;
497                         } else {
498                                 remain_cnt++;
499                         }
500                 }
501                 it = it->next;
502         }
503         RETVM_IF(del_cnt == 0, CONTACTS_ERROR_NO_DATA, "No Data: nothing deleted, remain_cnt : %d", remain_cnt);
504
505         *noti_list = result;
506
507         return remain_cnt;
508 }
509
510 int ctsvc_inotify_unsubscribe(const char *view_uri, contacts_db_changed_cb cb, void *user_data)
511 {
512         int ret, wd;
513         char *path = NULL;
514
515         RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
516         RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
517                         "System : __inoti_fd(%d) is invalid", __inoti_fd);
518
519         ret = __ctsvc_noti_get_file_path(view_uri, &path);
520         if (CONTACTS_ERROR_NONE != ret) {
521                 /* LCOV_EXCL_START */
522                 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
523                 return ret;
524                 /* LCOV_EXCL_STOP */
525         }
526
527         wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
528         if (-1 == wd) {
529                 /* LCOV_EXCL_START */
530                 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
531                 free(path);
532                 if (errno == EACCES)
533                         return CONTACTS_ERROR_PERMISSION_DENIED;
534                 return CONTACTS_ERROR_SYSTEM;
535                 /* LCOV_EXCL_STOP */
536         }
537
538         ret = __ctsvc_del_noti(&__noti_list, wd, view_uri, cb, user_data);
539         WARN_IF(ret < CONTACTS_ERROR_NONE, "__ctsvc_del_noti() Fail(%d)", ret);
540
541         if (0 == ret) {
542                 free(path);
543                 return inotify_rm_watch(__inoti_fd, wd);
544         }
545
546         /* LCOV_EXCL_START */
547         ret =  __ctsvc_inotify_watch(__inoti_fd, path);
548         free(path);
549         return ret;
550         /* LCOV_EXCL_STOP */
551 }
552
553 static void __clear_nslot_list(gpointer data, gpointer user_data)
554 {
555         noti_info *noti = (noti_info *)data;
556
557         free(noti->view_uri);
558         free(noti);
559 }
560
561 static inline gboolean __ctsvc_inotify_detach_handler(guint id)
562 {
563         return g_source_remove(id);
564 }
565
566 void ctsvc_inotify_close(void)
567 {
568         if (1 < __ctsvc_inoti_ref) {
569                 DBG("inotify ref count : %d", __ctsvc_inoti_ref);
570                 __ctsvc_inoti_ref--;
571                 return;
572         } else if (__ctsvc_inoti_ref < 1) {
573                 DBG("Please call connection API. inotify ref count : %d", __ctsvc_inoti_ref);
574                 return;
575         }
576
577         __ctsvc_inoti_ref--;
578
579         if (__inoti_handler) {
580                 __ctsvc_inotify_detach_handler(__inoti_handler);
581                 __inoti_handler = 0;
582         }
583
584         if (__noti_list) {
585                 g_slist_foreach(__noti_list, __clear_nslot_list, NULL);
586                 g_slist_free(__noti_list);
587                 __noti_list = NULL;
588         }
589
590         if (0 <= __inoti_fd) {
591                 close(__inoti_fd);
592                 __inoti_fd = -1;
593         }
594 }
595
596 static char* __ctsvc_inotify_get_username(uid_t uid)
597 {
598         struct passwd pwd;
599         struct passwd *pwd_result;
600         char tmp[CTSVC_STR_SHORT_LEN];
601
602         int ret = getpwuid_r(uid, &pwd, tmp, sizeof(tmp), &pwd_result);
603         if (ret != 0 || pwd_result == NULL) {
604                 /* LCOV_EXCL_START */
605                 ERR("getpwuid_r() fail");
606                 return NULL;
607                 /* LCOV_EXCL_STOP */
608         }
609
610         return SAFE_STRDUP(pwd.pw_name);
611 }
612
613
614 char*  ctsvc_inotify_makepath(const char *file)
615 {
616         RETV_IF(NULL == file, NULL);
617
618         static int user_name_max = 32;
619         int path_len = 0;
620         char *path = NULL;
621         uid_t uid = getuid();
622         char *user_name = NULL;
623
624 #ifdef _CONTACTS_IPC_CLIENT
625         if (ctsvc_client_is_in_system_session()) {
626                 /* LCOV_EXCL_START */
627                 if (CONTACTS_ERROR_NONE != ctsvc_client_get_active_uid(&uid))
628                         return NULL;
629                 /* LCOV_EXCL_STOP */
630         }
631 #endif
632
633         path_len = strlen(CTSVC_NOTI_PATH) + user_name_max + strlen(file) + 1;
634         path = calloc(1, path_len);
635         if (NULL == path) {
636                 ERR("calloc() fail");
637                 return NULL;
638         }
639
640         user_name = __ctsvc_inotify_get_username(uid);
641         if (user_name) {
642                 snprintf(path, path_len, CTSVC_NOTI_PATH"/%s", user_name, file);
643         } else {
644                 /* LCOV_EXCL_START */
645                 ERR("__ctsvc_inotify_get_username() fail");
646                 free(path);
647                 return NULL;
648                 /* LCOV_EXCL_STOP */
649         }
650
651         free(user_name);
652         return path;
653 }
654