Moved exception list to plugin
[platform/core/connectivity/stc-manager.git] / src / monitor / stc-app-lifecycle.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdbool.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <sys/socket.h>
25 #include <linux/netlink.h>
26 #include <linux/connector.h>
27 #include <linux/cn_proc.h>
28 #include <glib.h>
29
30 #include "stc-manager-gdbus.h"
31 #include "stc-app-lifecycle.h"
32 #include "helper-procfs.h"
33
34 typedef struct {
35         pid_t pid;
36 } proc_key_s;
37
38 typedef struct {
39         char cmdline[PROC_NAME_MAX];
40         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
41 } proc_value_s;
42
43 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
44         struct nlmsghdr nl_hdr;
45         struct __attribute__ ((__packed__)) {
46                 struct cn_msg cn_msg;
47                 enum proc_cn_mcast_op cn_mcast;
48         };
49 } nl_connector_msg_s;
50
51 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
52         struct nlmsghdr nl_hdr;
53         struct __attribute__ ((__packed__)) {
54                 struct cn_msg cn_msg;
55                 struct proc_event proc_ev;
56         };
57 } nl_connector_proc_event_s;
58
59 static int nl_connector_sock = -1;
60 static guint nl_connector_gsource_id = 0;
61 static GTree *proc_tree;
62
63
64 static gboolean __process_nl_connector_message(GIOChannel *source,
65                                                GIOCondition condition,
66                                                gpointer user_data);
67
68 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
69                                    gpointer UNUSED user_data)
70 {
71         proc_key_s *key_a = (proc_key_s *)a;
72         proc_key_s *key_b = (proc_key_s *)b;
73
74         return key_a->pid - key_b->pid;
75 }
76
77 static void __proc_tree_value_free(gpointer data)
78 {
79         proc_value_s *value = (proc_value_s *)data;
80
81         FREE(value);
82 }
83
84 static void __proc_tree_key_free(gpointer data)
85 {
86         proc_key_s *key = (proc_key_s *)data;
87
88         FREE(key);
89 }
90
91 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
92 {
93         proc_value_s *lookup;
94
95         if (proc_tree == NULL) {
96                 STC_LOGE("tree is null");
97                 return NULL;
98         }
99
100         lookup = g_tree_lookup(proc_tree, key);
101         return lookup;
102 }
103
104 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
105                                           gpointer data)
106 {
107         proc_key_s *proc_key = (proc_key_s *)key;
108         proc_value_s *proc_value = (proc_value_s *)value;
109
110         STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [\033[1;35m%s\033[0;m] "
111                 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
112                 proc_value->status[PROC_STATUS_PPID], proc_value->cmdline);
113
114         return FALSE;
115 }
116
117 static void __proc_tree_printall(void)
118 {
119         g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL);
120 }
121
122 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
123 {
124         proc_value_s *parent = NULL;
125         proc_value_s *lookup = value;
126
127         do {
128                 proc_key_s key;
129                 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
130                 lookup = __proc_tree_lookup(&key);
131                 if (lookup != NULL)
132                         parent = lookup;
133         } while (lookup);
134
135         if (STC_DEBUG_LOG) {
136                 if (parent != NULL)
137                         STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] pid[%s] "
138                                 "ppid[\033[1;35m%s\033[0;m] cmdline[\033[0;34m%s\033[0;m] name[%s]",
139                                 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
140                                 parent->status[PROC_STATUS_PPID], parent->cmdline,
141                                 parent->status[PROC_STATUS_NAME]);
142         }
143
144         return parent;
145 }
146
147 static void __proc_tree_add(proc_key_s *key,
148                             proc_value_s *value)
149 {
150         proc_value_s *lookup;
151         proc_value_s *parent;
152
153         if (proc_tree == NULL) {
154                 STC_LOGE("tree is null");
155                 return;
156         }
157
158         lookup = g_tree_lookup(proc_tree, key);
159         if (lookup) {
160                 if (STC_DEBUG_LOG)
161                         STC_LOGD("LOOKUP: tgid[\033[1;33m%s\033[0;m] pid[%s] ppid[\033[1;35m%s\033[0;m] "
162                                 "cmdline[\033[0;34m%s\033[0;m] name[%s]", lookup->status[PROC_STATUS_TGID],
163                                 lookup->status[PROC_STATUS_PID], lookup->status[PROC_STATUS_PPID],
164                                 lookup->cmdline, lookup->status[PROC_STATUS_NAME]);
165                 return;
166         }
167
168         STC_LOGD("cmdline [%s] pid[%s] ppid[%s]", value->cmdline,
169                 value->status[PROC_STATUS_PID], value->status[PROC_STATUS_PPID]);
170
171         g_tree_insert(proc_tree, key, value);
172
173         if (STC_DEBUG_LOG)
174                 __proc_tree_printall();
175
176         parent = __proc_tree_find_parent(value);
177         if (parent != NULL)
178                 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
179                         parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
180         else
181                 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
182                         value->cmdline, value->cmdline, STC_APP_TYPE_SERVICE);
183 }
184
185 static void __proc_tree_remove(const proc_key_s *key)
186 {
187         if (proc_tree == NULL) {
188                 STC_LOGE("tree is null");
189                 return;
190         }
191
192         stc_manager_app_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
193                                        NULL, STC_APP_TYPE_NONE);
194
195         g_tree_remove(proc_tree, key);
196
197         if (STC_DEBUG_LOG)
198                 __proc_tree_printall();
199 }
200
201 static gboolean __check_excn(char *cmdline)
202 {
203         stc_error_e ret = STC_ERROR_NONE;
204
205         if (cmdline[0] == '(')
206                 return TRUE;
207
208         ret = stc_monitor_check_excn_by_cmdline(cmdline);
209         if (ret == STC_ERROR_UNINITIALIZED ||
210                 ret == STC_ERROR_NO_DATA)
211                 return FALSE;
212         else
213                 return TRUE;
214 }
215
216 static void __open_nl_connector_sock(void)
217 {
218         __STC_LOG_FUNC_ENTER__;
219         GIOChannel *gio = NULL;
220
221         if (nl_connector_sock != -1 &&
222             nl_connector_gsource_id != 0) {
223                 STC_LOGE("Socket is already open");
224                 __STC_LOG_FUNC_EXIT__;
225                 return;
226         }
227
228         if (nl_connector_sock != -1) {
229                 close(nl_connector_sock);
230                 nl_connector_sock = -1;
231         }
232
233         if (nl_connector_gsource_id != 0) {
234                 g_source_remove(nl_connector_gsource_id);
235                 nl_connector_gsource_id = 0;
236         }
237
238         nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
239         if (nl_connector_sock == -1) {
240                 __STC_LOG_FUNC_EXIT__;
241                 return;
242         }
243
244         gio = g_io_channel_unix_new(nl_connector_sock);
245         nl_connector_gsource_id =
246                 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
247                                (GIOFunc) __process_nl_connector_message,
248                                NULL);
249         g_io_channel_unref(gio);
250         __STC_LOG_FUNC_EXIT__;
251 }
252
253 static void __close_nl_connector_sock(void)
254 {
255         __STC_LOG_FUNC_ENTER__;
256         if (nl_connector_sock != -1) {
257                 close(nl_connector_sock);
258                 nl_connector_sock = -1;
259         }
260
261         if (nl_connector_gsource_id != 0) {
262                 g_source_remove(nl_connector_gsource_id);
263                 nl_connector_gsource_id = 0;
264         }
265         __STC_LOG_FUNC_EXIT__;
266 }
267
268 static void __reopen_nl_connector_sock(void)
269 {
270         __close_nl_connector_sock();
271         __open_nl_connector_sock();
272 }
273
274 stc_error_e stc_manager_app_status_changed(stc_cmd_type_e cmd,
275                                            pid_t pid,
276                                            const gchar *app_id,
277                                            const gchar *pkg_id,
278                                            stc_app_type_e app_type)
279 {
280         stc_error_e ret = STC_ERROR_NONE;
281
282         if (pkg_id && app_id)
283                 STC_LOGD("cmd [%d] pkgid [%s] appid [%s] pid[%d] type [%d]",
284                         cmd, pkg_id, app_id, pid, app_type);
285
286         switch (cmd) {
287         case STC_CMD_SET_FOREGRD:
288         {
289                 stc_app_key_s app_key;
290                 stc_app_value_s app_value;
291                 stc_process_key_s proc_key;
292                 stc_process_value_s proc_value;
293
294                 memset(&app_key, 0, sizeof(stc_app_key_s));
295                 memset(&app_value, 0, sizeof(stc_app_value_s));
296                 memset(&proc_key, 0, sizeof(stc_process_key_s));
297                 memset(&proc_value, 0, sizeof(stc_process_value_s));
298
299                 app_key.pkg_id = g_strdup(pkg_id);
300                 app_key.app_id = g_strdup(app_id);
301
302                 app_value.type = app_type;
303                 app_value.processes = NULL;
304
305                 proc_key.pid = pid;
306
307                 proc_value.ground = STC_APP_STATE_FOREGROUND;
308
309                 stc_monitor_application_add(app_key, app_value);
310                 stc_monitor_process_add(app_key, proc_key, proc_value);
311                 stc_monitor_process_update_ground(app_key, proc_key,
312                                                   STC_APP_STATE_FOREGROUND);
313
314                 FREE(app_key.pkg_id);
315                 FREE(app_key.app_id);
316                 break;
317         }
318         case STC_CMD_SET_BACKGRD:
319         {
320                 stc_app_key_s app_key;
321                 stc_app_value_s app_value;
322                 stc_process_key_s proc_key;
323                 stc_process_value_s proc_value;
324
325                 memset(&app_key, 0, sizeof(stc_app_key_s));
326                 memset(&app_value, 0, sizeof(stc_app_value_s));
327                 memset(&proc_key, 0, sizeof(stc_process_key_s));
328                 memset(&proc_value, 0, sizeof(stc_process_value_s));
329
330                 app_key.pkg_id = g_strdup(pkg_id);
331                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
332                                              NULL);
333
334                 app_value.type = app_type;
335                 app_value.processes = NULL;
336
337                 proc_key.pid = pid;
338
339                 proc_value.ground = STC_APP_STATE_BACKGROUND;
340
341                 stc_monitor_application_add(app_key, app_value);
342                 stc_monitor_process_add(app_key, proc_key, proc_value);
343                 stc_monitor_process_update_ground(app_key, proc_key,
344                                                   STC_APP_STATE_BACKGROUND);
345
346                 FREE(app_key.pkg_id);
347                 FREE(app_key.app_id);
348                 break;
349         }
350         case STC_CMD_SET_SERVICE_LAUNCHED:
351         {
352                 stc_app_key_s app_key;
353                 stc_app_value_s app_value;
354                 stc_process_key_s proc_key;
355                 stc_process_value_s proc_value;
356
357                 memset(&app_key, 0, sizeof(stc_app_key_s));
358                 memset(&app_value, 0, sizeof(stc_app_value_s));
359                 memset(&proc_key, 0, sizeof(stc_process_key_s));
360                 memset(&proc_value, 0, sizeof(stc_process_value_s));
361
362                 app_key.pkg_id = g_strdup(pkg_id);
363                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
364                                              NULL);
365
366                 app_value.type = app_type;
367                 app_value.processes = NULL;
368
369                 proc_key.pid = pid;
370
371                 /* services will run always in background. */
372                 proc_value.ground = STC_APP_STATE_BACKGROUND;
373
374                 stc_monitor_application_add(app_key, app_value);
375                 stc_monitor_process_add(app_key, proc_key, proc_value);
376
377                 FREE(app_key.pkg_id);
378                 g_free(app_key.app_id);
379                 break;
380         }
381         case STC_CMD_SET_TERMINATED:
382         {
383                 stc_monitor_process_remove(pid);
384                 break;
385         }
386         default:
387                 STC_LOGE("Unhandled command");
388                 ret = STC_ERROR_INVALID_PARAMETER;
389         }
390
391         return ret;
392 }
393
394 static void __process_event_fork(int tgid, int pid)
395 {
396         char cmdline[PROC_NAME_MAX] = {0, };
397         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
398
399         memset(status, 0x0, sizeof(status));
400
401         if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
402             STC_ERROR_NONE == proc_get_status(pid, status)) {
403
404                 if (__check_excn(cmdline)) {
405                         if (STC_DEBUG_LOG)
406                                 STC_LOGD("[%s] monitoring is excepted", cmdline);
407                         return;
408                 }
409
410                 unsigned int i;
411                 proc_key_s *key;
412                 proc_value_s *value;
413
414                 key = MALLOC0(proc_key_s, 1);
415                 if (key == NULL) {
416                         STC_LOGE("memory allocation failed");
417                         return;
418                 }
419
420                 value = MALLOC0(proc_value_s, 1);
421                 if (value == NULL) {
422                         STC_LOGE("memory allocation failed");
423                         FREE(key);
424                         return;
425                 }
426
427                 key->pid = tgid;
428                 for (i = 0; i < PROC_STATUS_CNT; ++i)
429                         g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
430                 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
431
432                 if (STC_DEBUG_LOG) {
433                         STC_LOGD("\033[1;34mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
434                                 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
435                         STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
436                                 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
437                                 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
438                 }
439
440                 __proc_tree_add(key, value);
441         }
442 }
443
444 static void __process_event_exec(int tgid, int pid)
445 {
446         char cmdline[PROC_NAME_MAX] = {0, };
447         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
448
449         memset(status, 0x0, sizeof(status));
450
451         if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
452             STC_ERROR_NONE == proc_get_status(pid, status)) {
453
454                 if (__check_excn(cmdline)) {
455                         if (STC_DEBUG_LOG)
456                                 STC_LOGD("[%s] monitoring is excepted", cmdline);
457                         return;
458                 }
459
460                 unsigned int i;
461                 proc_key_s *key;
462                 proc_value_s *value;
463
464                 key = MALLOC0(proc_key_s, 1);
465                 if (key == NULL) {
466                         STC_LOGE("memory allocation failed");
467                         return;
468                 }
469
470                 value = MALLOC0(proc_value_s, 1);
471                 if (value == NULL) {
472                         STC_LOGE("memory allocation failed");
473                         FREE(key);
474                         return;
475                 }
476
477                 key->pid = tgid;
478                 for (i = 0; i < PROC_STATUS_CNT; ++i)
479                         g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
480                 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
481
482                 if (STC_DEBUG_LOG) {
483                         STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
484                                 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
485                         STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
486                                 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
487                                 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
488                 }
489
490                 __proc_tree_add(key, value);
491         }
492 }
493
494 static void __process_event_exit(int tgid, int pid, int exit_code)
495 {
496         proc_key_s key;
497         proc_value_s *lookup;
498
499         if (tgid != pid)
500                 return;
501
502         key.pid = tgid;
503         lookup = __proc_tree_lookup(&key);
504         if (lookup == NULL) /* unmonitored process */
505                 return;
506
507         if (STC_DEBUG_LOG)
508                 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
509                         "pid[%d] exitcode[\033[0;31m%d\033[0;m]", tgid, pid, exit_code);
510
511         __proc_tree_remove(&key);
512 }
513
514 static gboolean __process_nl_connector_message(GIOChannel *source,
515                                                GIOCondition condition,
516                                                gpointer user_data)
517 {
518         int ret;
519         int sock = g_io_channel_unix_get_fd(source);
520         nl_connector_proc_event_s msg;
521
522         if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
523             (condition & G_IO_NVAL)) {
524                 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
525
526                 STC_LOGE("Netlink Connector socket received G_IO event, closing"
527                          " socket. G_IO_ERR [%d], G_IO_HUP [%d], G_IO_NVAL [%s]",
528                          (condition & G_IO_ERR), (condition & G_IO_HUP),
529                          (condition & G_IO_NVAL));
530                 __reopen_nl_connector_sock();
531                 __STC_LOG_FUNC_EXIT__;
532                 return FALSE;
533         }
534
535         memset(&msg, 0, sizeof(nl_connector_proc_event_s));
536
537         ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
538         if (ret == 0) {
539                 __STC_LOG_FUNC_EXIT__;
540                 return TRUE;
541         }
542
543         switch (msg.proc_ev.what) {
544         case PROC_EVENT_FORK:
545                 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
546                                      msg.proc_ev.event_data.fork.child_pid);
547                 break;
548         case PROC_EVENT_EXEC:
549                 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
550                                      msg.proc_ev.event_data.exec.process_pid);
551                 break;
552         case PROC_EVENT_EXIT:
553                 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
554                                      msg.proc_ev.event_data.exit.process_pid,
555                                      msg.proc_ev.event_data.exit.exit_code);
556                 break;
557         default:
558                 ; /* Do nothing */
559         }
560
561         return TRUE;
562 }
563
564 static int __subscribe_proc_events(void)
565 {
566         __STC_LOG_FUNC_ENTER__;
567         nl_connector_msg_s msg;
568         int ret;
569         int sock = nl_connector_sock;
570
571         if (sock == -1) {
572                 __STC_LOG_FUNC_EXIT__;
573                 return -1;
574         }
575
576         memset(&msg, 0, sizeof(nl_connector_msg_s));
577
578         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
579         msg.nl_hdr.nlmsg_pid = getpid();
580         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
581
582         msg.cn_msg.id.idx = CN_IDX_PROC;
583         msg.cn_msg.id.val = CN_VAL_PROC;
584         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
585
586         msg.cn_mcast = PROC_CN_MCAST_LISTEN;
587
588         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
589         if (ret == -1) {
590                 STC_LOGE("Error sending netlink connector message");
591                 __STC_LOG_FUNC_EXIT__;
592                 return -1;
593         }
594
595         __STC_LOG_FUNC_EXIT__;
596         return 0;
597 }
598
599 static int __unsubscribe_proc_events(void)
600 {
601         __STC_LOG_FUNC_ENTER__;
602         nl_connector_msg_s msg;
603         int ret;
604         int sock = nl_connector_sock;
605
606         if (sock == -1) {
607                 __STC_LOG_FUNC_EXIT__;
608                 return -1;
609         }
610
611         memset(&msg, 0, sizeof(nl_connector_msg_s));
612
613         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
614         msg.nl_hdr.nlmsg_pid = getpid();
615         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
616
617         msg.cn_msg.id.idx = CN_IDX_PROC;
618         msg.cn_msg.id.val = CN_VAL_PROC;
619         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
620
621         msg.cn_mcast = PROC_CN_MCAST_IGNORE;
622
623         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
624         if (ret == -1) {
625                 STC_LOGE("Error sending netlink connector message");
626                 __STC_LOG_FUNC_EXIT__;
627                 return -1;
628         }
629
630         __STC_LOG_FUNC_EXIT__;
631         return 0;
632 }
633
634 void stc_app_lifecycle_monitor_init(void)
635 {
636         __STC_LOG_FUNC_ENTER__;
637
638         proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
639                                     __proc_tree_key_free,
640                                     __proc_tree_value_free);
641
642         /* TODO: Fill proc tree with current procfs state */
643
644         __open_nl_connector_sock();
645         __subscribe_proc_events();
646         __STC_LOG_FUNC_EXIT__;
647 }
648
649 void stc_app_lifecycle_monitor_deinit(void)
650 {
651         __STC_LOG_FUNC_ENTER__;
652
653         if (nl_connector_sock == -1) {
654                 STC_LOGE("socket already closed");
655                 return;
656         }
657
658         __unsubscribe_proc_events();
659         __close_nl_connector_sock();
660
661         g_tree_destroy(proc_tree);
662         proc_tree = NULL;
663
664         __STC_LOG_FUNC_EXIT__;
665 }