Make pkg_type to lower case when queuing
[platform/core/appfw/pkgmgr-server.git] / src / pm-queue.c
1 /*
2  * slp-pkgmgr
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Jayoun Lee <airjany@samsung.com>, Sewook Park <sewook7.park@samsung.com>,
7  * Jaeho Lee <jaeho81.lee@samsung.com>, Shobhit Srivastava <shobhit.s@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <ctype.h>
31
32 #include "pkgmgr-server.h"
33 #include "pm-queue.h"
34
35 #ifndef BACKEND_DIR
36 #define BACKEND_DIR "/etc/package-manager/backend"
37 #endif
38
39 static pm_queue_data *__get_head_from_pkgtype(const char *pkg_type);
40 static void __update_head_from_pkgtype(pm_queue_data *data);
41 static int __entry_exist(char *backend);
42 static int __is_pkg_supported(const char *pkgtype);
43
44 queue_info_map *start = NULL;
45 int entries = 0;
46 int slot = 0;
47 int num_of_backends = 0;
48
49 /*Function to check whether a particular package type
50 is supported or not. It parses the queue info map
51 to get the information.
52 It will prevent the accidental hanging of server.
53 Returns 1 if found.*/
54 static int __is_pkg_supported(const char *pkgtype)
55 {
56         queue_info_map *ptr = NULL;
57         ptr = start;
58         int i = 0;
59         for(i = 0; i < entries; i++)
60         {
61                 if (!strncmp(ptr->pkgtype, pkgtype, MAX_PKG_TYPE_LEN))
62                         return 1;
63                 else {
64                         ptr++;
65                         continue;
66                 }
67         }
68         return 0;
69 }
70
71 /*tells whether a particular backend exists in the
72 * info map or not.
73 * on Success it return the queue slot of the already present entry
74 * on Failure -1 is returned*/
75 static int __entry_exist(char *backend)
76 {
77         queue_info_map *ptr = NULL;
78         ptr = start;
79         int i = 0;
80         for(i = 0; i < entries; i++)
81         {
82                 if (!strncmp(ptr->backend, backend, MAX_PKG_NAME_LEN))
83                         return ptr->queue_slot;
84                 else {
85                         ptr++;
86                         continue;
87                 }
88         }
89         return -1;
90 }
91
92 /*In case of first push, it updates the queue head
93 and copies it to all duplicate entries in queue info map*/
94 static void __update_head_from_pkgtype(pm_queue_data *data)
95 {
96         queue_info_map *ptr = NULL;
97         ptr = start;
98         int slot = -1;
99         int i = 0;
100         for(i = 0; i < entries; i++)
101         {
102                 if (!strncmp(ptr->pkgtype, data->msg->pkg_type, MAX_PKG_TYPE_LEN)) {
103                         ptr->head = data;
104                         slot = ptr->queue_slot;
105                 }
106                 else {
107                         ptr++;
108                         continue;
109                 }
110         }
111         /*update head for each duplicate entry*/
112         ptr = start;
113         for(i = 0; i < entries; i++)
114         {
115                 if(ptr->queue_slot == slot && !ptr->head) {
116                         ptr->head = data;
117                 }
118                 ptr++;
119         }
120         return;
121 }
122
123 /*Gets the queue head based on pkg type*/
124 static pm_queue_data *__get_head_from_pkgtype(const char *pkg_type)
125 {
126         queue_info_map *ptr = NULL;
127         ptr = start;
128         int i = 0;
129         for(i = 0; i < entries; i++)
130         {
131                 if (!strncmp(ptr->pkgtype, pkg_type, MAX_PKG_TYPE_LEN))
132                         return ptr->head;
133                 else {
134                         ptr++;
135                         continue;
136                 }
137         }
138         return NULL;
139
140 }
141
142 int _pm_queue_init(void)
143 {
144         /*Find the num of backends currently supported and initialize
145         that many queues. It is dynamically determined.*/
146         struct dirent **namelist;
147         struct stat fileinfo;
148         queue_info_map *ptr = NULL;
149         int n = 0;
150         int c = 0;
151         int i = 0;
152         int ret = 0;
153         char abs_filename[MAX_PKG_NAME_LEN] = {'\0'};
154         char buf[MAX_PKG_NAME_LEN] = {'\0'};
155         n = scandir(BACKEND_DIR, &namelist, NULL, alphasort);
156         if (n < 0) {
157                 perror("scandir");
158                 return -1;
159         }
160         i = n;
161         /*Find number of backends (symlinks + executables)
162         The backend dir should not conatin any other file except the backends.*/
163         while(n--)
164         {
165                 if(!strcmp(namelist[n]->d_name, ".") ||
166                         !strcmp(namelist[n]->d_name, ".."))
167                                 continue;
168                 snprintf(abs_filename, MAX_PKG_NAME_LEN, "%s/%s",
169                         BACKEND_DIR, namelist[n]->d_name);
170                 if (lstat(abs_filename, &fileinfo)) {
171                         perror("lstat");
172                         continue;
173                 }
174                 if (S_ISDIR(fileinfo.st_mode))
175                         continue;
176                 c++;
177                 memset(abs_filename, 0x00, MAX_PKG_NAME_LEN);
178         }
179         /*Add entries to info map.*/
180         ptr = (queue_info_map*)calloc(c , sizeof(queue_info_map));
181         memset(ptr, '\0', c * sizeof(queue_info_map));
182         start = ptr;
183         for(n = 0; n < c ; n++)
184         {
185                 ptr->backend[0] = '\0';
186                 ptr->head = NULL;
187                 ptr->queue_slot = -2;/*-1 can be error return*/
188                 ptr->pkgtype[0] = '\0';
189                 ptr++;
190         }
191         n = i;
192         ptr = start;
193         while(n--)
194         {
195                 if(!strcmp(namelist[n]->d_name, ".") ||
196                         !strcmp(namelist[n]->d_name, ".."))
197                                 continue;
198                 snprintf(abs_filename, MAX_PKG_NAME_LEN, "%s/%s",
199                         BACKEND_DIR, namelist[n]->d_name);
200                 if (lstat(abs_filename, &fileinfo) < 0) {
201                         perror(abs_filename);
202                         return -1;
203                 }
204                 if (S_ISDIR(fileinfo.st_mode))
205                         continue;
206                 /*Found backend*/
207                 if (S_ISLNK(fileinfo.st_mode)) {
208                         /*found a symlink*/
209                         ret = readlink(abs_filename, buf, MAX_PKG_NAME_LEN - 1);
210                         if (ret == -1) {
211                                 perror("readlink");
212                                 return -1;
213                         }
214                         buf[ret] = '\0';
215                 }
216                 /*executable*/
217                 else {
218                         snprintf(buf, sizeof(buf), "%s", abs_filename);
219                 }
220                 ret = __entry_exist(buf);
221                 if (ret == -1) {
222                         snprintf(ptr->backend, sizeof(ptr->backend), "%s", buf);
223                         snprintf(ptr->pkgtype, sizeof(ptr->pkgtype), "%s", namelist[n]->d_name);
224                         ptr->queue_slot = slot;
225                         ptr->head = NULL;
226                         entries++;
227                         slot++;
228                         ptr++;
229                 }
230                 else {
231                         snprintf(ptr->backend, sizeof(ptr->backend), "%s", buf);
232                         snprintf(ptr->pkgtype, sizeof(ptr->pkgtype), "%s", namelist[n]->d_name);
233                         ptr->queue_slot = ret;
234                         ptr->head = NULL;
235                         entries++;
236                         ptr++;
237                 }
238                 free(namelist[n]);
239                 memset(buf, 0x00, MAX_PKG_NAME_LEN);
240                 continue;
241         }
242         free(namelist);
243         num_of_backends = slot;
244
245 #ifdef DEBUG_INFO
246         /*Debug info*/
247         DBG("Queue Info Map");
248         DBG("Number of Backends is %d", num_of_backends);
249         DBG("Number of Entries is %d", entries);
250         DBG("Backend\tType\tSlot\tHead");
251         ptr = start;
252         for(n = 0; n < entries; n++)
253         {
254                 DBG("%s\t%s\t%d\t%p", ptr->backend, ptr->pkgtype, ptr->queue_slot, ptr->head);
255                 ptr++;
256         }
257 #endif
258
259         return 0;
260 }
261
262 static void __convert_to_lower_case(char *dst, const char *src)
263 {
264         int i;
265
266         for (i = 0; src[i] != '\0'; i++)
267                 dst[i] = tolower(src[i]);
268         dst[i] = '\0';
269 }
270
271 int _pm_queue_push(uid_t uid, const char *req_id, int req_type,
272                 const char *queue_type, const char *pkgid, const char *args)
273 {
274         pm_queue_data *data = NULL;
275         pm_queue_data *cur = NULL;
276         pm_queue_data *tmp = NULL;
277         char type[MAX_PKG_NAME_LEN];
278         int ret;
279
280         __convert_to_lower_case(type, queue_type);
281
282         ret = __is_pkg_supported(type);
283         if (ret == 0)
284                 return -1;
285
286         cur = __get_head_from_pkgtype(type);
287         tmp = cur;
288
289         /* TODO: use glist */
290         data = _add_node();
291         if (!data) { /* fail to allocate mem */
292                 ERR("Fail to allocate memory\n");
293                 return -1;
294         }
295
296         snprintf(data->msg->req_id, sizeof(data->msg->req_id), "%s", req_id);
297         data->msg->req_type = req_type;
298         data->msg->uid = uid;
299         snprintf(data->msg->pkg_type, sizeof(data->msg->pkg_type), "%s", type);
300         snprintf(data->msg->pkgid, sizeof(data->msg->pkgid), "%s", pkgid);
301         snprintf(data->msg->args, sizeof(data->msg->args), "%s", args);
302
303         data->next = NULL;
304
305         if (cur == NULL) {
306                 /* first push */
307                 cur = data;
308                 __update_head_from_pkgtype(data);
309         }
310         else {
311                 while (tmp->next)
312                         tmp = tmp->next;
313
314                 tmp->next = data;
315         }
316         return 0;
317 }
318
319 /*pop request from queue slot "position" */
320 pm_dbus_msg *_pm_queue_pop(int position)
321 {
322         pm_dbus_msg *ret;
323         pm_queue_data *cur = NULL;
324         pm_queue_data *saveptr = NULL;
325         queue_info_map *ptr = NULL;
326         int i = 0;
327
328         ret = (pm_dbus_msg *) malloc(sizeof(pm_dbus_msg));
329         if (!ret) {
330                 ERR("Mem alloc error");
331                 return NULL;
332         }
333         memset(ret, 0x00, sizeof(pm_dbus_msg));
334         ptr = start;
335         for(i = 0; i < entries; i++)
336         {
337                 if (ptr->queue_slot == position) {
338                                 cur = ptr->head;
339                                 break;
340                 }
341                 ptr++;
342         }
343
344         if (!cur) {             /* queue is empty */
345                 ret->req_type = -1;
346                 return ret;
347         }
348
349         snprintf(ret->req_id, sizeof(ret->req_id), "%s", cur->msg->req_id);
350         ret->req_type = cur->msg->req_type;
351         ret->uid = cur->msg->uid;
352         snprintf(ret->pkg_type, sizeof(ret->pkg_type), "%s", cur->msg->pkg_type);
353         snprintf(ret->pkgid, sizeof(ret->pkgid), "%s", cur->msg->pkgid);
354         snprintf(ret->args, sizeof(ret->args), "%s", cur->msg->args);
355
356         ptr->head = cur->next;
357         saveptr = ptr->head;
358         cur->next = NULL;
359         free(cur->msg);
360         free(cur);
361         /*update head for each duplicate queue entry*/
362         ptr = start;
363         for(i = 0; i < entries; i++)
364         {
365                 if(ptr->queue_slot == position) {
366                         ptr->head = saveptr;
367                 }
368                 ptr++;
369         }
370         return ret;
371 }
372
373 /*populate an array of all queue heads and delete them one by one*/
374 void _pm_queue_final()
375 {
376         int c = 0;
377         int i = 0;
378         int slot = -1;
379         pm_queue_data *cur = NULL;
380         pm_queue_data *tail = NULL;
381         pm_queue_data *prev = NULL;
382         pm_queue_data *head[MAX_QUEUE_NUM] = {NULL,};
383         queue_info_map *ptr = NULL;
384         ptr = start;
385
386         for(i = 0; i < num_of_backends; i++)
387         {
388                 head[i] = NULL;
389         }
390
391         for(i = 0; i < entries; i++)
392         {
393                 if (ptr->queue_slot <= slot) {
394                         ptr++;
395                         continue;
396                 }
397                 else {
398                         head[c] = ptr->head;
399                         slot = ptr->queue_slot;
400                         c++;
401                         ptr++;
402                 }
403         }
404
405         c = 0;
406         while(c < num_of_backends) {
407                 if (!head[c]) { /* in case of head is NULL */
408                         ERR("queue is NULL");
409                         c = c + 1;
410                         continue;
411                 }
412
413                 while (head[c]->next) {
414                         cur = head[c]->next;
415
416                         while (cur->next) {
417                                 prev = cur;
418                                 cur = cur->next;
419                         }
420
421                         tail = cur;
422
423                         free(tail->msg);
424                         free(tail);
425                         prev->next = NULL;
426                 }
427
428                 free(head[c]->msg);
429                 free(head[c]);
430
431                 head[c] = NULL;
432                 c = c + 1;
433         }
434         /*Free the info map*/
435         if (start) {
436                 free(start);
437                 start = NULL;
438         }
439 }
440
441 pm_queue_data *_add_node()
442 {
443         pm_queue_data *newnode = NULL;
444
445         newnode = (pm_queue_data *) malloc(sizeof(pm_queue_data));
446         if (!newnode) { /* if NULL */
447                 ERR("Mem alloc error");
448                 return NULL;
449         }
450         memset(newnode, 0x00, sizeof(pm_queue_data));
451
452         newnode->msg = (pm_dbus_msg *) malloc(sizeof(pm_dbus_msg));
453         if (!newnode->msg) {
454                 ERR("Mem alloc error");
455                 free(newnode);
456                 return NULL;
457         }
458         memset(newnode->msg, 0x00, sizeof(pm_dbus_msg));
459
460         return newnode;
461 }
462
463 void _pm_queue_delete(pm_dbus_msg *item)
464 {
465         /* Assume that pacakge name is unique */
466         pm_queue_data *cur = NULL;
467         pm_queue_data *prev = NULL;
468         cur = __get_head_from_pkgtype(item->pkg_type);
469         prev = cur;
470         if (cur) {
471                 while (cur->next) {
472                         if (!strcmp(item->pkgid, cur->msg->pkgid)) {
473                                 prev->next = cur->next;
474                                 free(cur->msg);
475                                 free(cur);
476                                 break;
477                         }
478                         prev = cur;
479                         cur = cur->next;
480                 }
481         }
482 }
483
484 void _save_queue_status(pm_dbus_msg *item, char *status)
485 {
486         FILE *fp_status = NULL;
487
488         fp_status = fopen(STATUS_FILE, "w");    /* overwrite always */
489         if (!fp_status) {
490                 ERR("Can't open status file:%s", STATUS_FILE);
491                 return;
492         }
493
494         fprintf(fp_status, "%s\n", status);
495         fprintf(fp_status, "%s\n", item->pkg_type);
496
497         fsync(fileno(fp_status));
498
499         fclose(fp_status);
500 }
501
502 void _print_queue(int position)
503 {
504         pm_queue_data *cur = NULL;
505         queue_info_map *ptr = start;
506         int i = 0;
507         for(i =0; i < entries; i++)
508         {
509                 if (ptr->queue_slot == position) {
510                                 cur = ptr->head;
511                                 break;
512                 }
513                 ptr++;
514         }
515         int index = 1;
516         if (!cur) {
517                 return;
518         }
519
520         while (cur) {
521                 index++;
522                 cur = cur->next;
523         }
524 }