tizen 2.3 release
[kernel/api/system-resource.git] / src / network / net-cls-cgroup.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2000 - 2013 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 /*
21  * @file classid-helper.c
22  *
23  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
24  *
25  */
26
27 #include "appid-helper.h"
28 #include "net-cls-cgroup.h"
29 #include "cgroup.h"
30 #include "const.h"
31 #include "data_usage.h"
32 #include "errors.h"
33 #include "file-helper.h"
34 #include "macro.h"
35 #include "trace.h"
36
37 #include <dirent.h>
38 #include <glib.h>
39 #include <stdio.h>
40 #include <string.h>
41
42 #define CUR_CLASSID_PATH "/tmp/cur_classid"
43 #define CLASSID_FILE_NAME "/net_cls.classid"
44 #define PATH_TO_NET_CGROUP_DIR "/sys/fs/cgroup/net_cls"
45
46 struct task_classid {
47         GArray *pids;
48         int pid_count;
49         u_int32_t classid;
50         char cgroup_name[MAX_NAME_LENGTH];      /*in combination it's package name */
51 };
52
53 typedef GArray task_classid_array;
54 static task_classid_array *tasks_classids;
55
56 static int read_uint(FILE *handler, u_int32_t *out)
57 {
58         return fscanf(handler, "%u", out);
59 }
60
61 static int read_int(FILE *handler, int *out)
62 {
63         return fscanf(handler, "%d", out);
64 }
65
66 static u_int32_t produce_classid(void)
67 {
68         u_int32_t classid = RESOURCED_RESERVED_CLASSID_MAX;
69         int ret = fread_int(CUR_CLASSID_PATH, &classid);
70         if (ret < 0)
71                 ETRACE_RET_ERRCODE_MSG(ret, "Can not read current classid");
72         ret = fwrite_uint(CUR_CLASSID_PATH, ++classid);
73         if (ret < 0)
74                 ETRACE_RET_ERRCODE_MSG(ret, "Can not write classid");
75
76         return classid;
77 }
78
79 static int place_classid_to_cgroup(const char *cgroup, const char *subdir,
80                                    u_int32_t *classid)
81 {
82         char buf[MAX_PATH_LENGTH];
83         u_int32_t generated_classid = produce_classid();
84         if (classid)
85                 *classid = generated_classid;
86
87         snprintf(buf, sizeof(buf), "%s/%s", cgroup, subdir);
88         return cgroup_write_node(buf, CLASSID_FILE_NAME, generated_classid);
89 }
90
91 static u_int32_t get_classid_from_cgroup(const char *cgroup, const char *subdir)
92 {
93         char buf[MAX_PATH_LENGTH];
94         u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
95         snprintf(buf, sizeof(buf), "%s/%s", cgroup, subdir);
96
97         int ret = cgroup_read_node(buf, CLASSID_FILE_NAME, &classid);
98         if (ret < 0)
99                 ETRACE_RET_ERRCODE_MSG(ret, "Cant read classid from cgroup %s",
100                         buf);
101         return classid;
102 }
103
104 static void
105 populate_classids_with_pids(const char *dir_name_buf, size_t dir_name_buf_len,
106                   const char *cgroup_name_buf,
107                   task_classid_array **tasks_classids_list)
108 {
109         char file_name_buf[MAX_PATH_LENGTH];
110         FILE *handler = 0;
111         struct task_classid tc;
112         memset(&tc, 0, sizeof(struct task_classid));
113         tc.pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
114         pid_t pid_for_read = 0;
115
116         /* first part of path */
117         snprintf(file_name_buf, sizeof(file_name_buf), "%s%s", dir_name_buf,
118                 CLASSID_FILE_NAME);
119         handler = fopen(file_name_buf, "r");
120
121         if (!handler) {
122                 _E("can't open %s file\n", file_name_buf);
123                 return;
124         }
125
126         if (sizeof(tc.cgroup_name) < strlen(cgroup_name_buf))
127                 _SE("not enought buffer for %s", cgroup_name_buf);
128         else
129                 strcpy(tc.cgroup_name, cgroup_name_buf);
130
131         if (read_uint(handler, &tc.classid) < 0)
132                 _E("can't read classid from file %s\n", file_name_buf);
133
134         fclose(handler);
135
136         strncpy(file_name_buf + dir_name_buf_len, TASK_FILE_NAME,
137                 dir_name_buf_len + sizeof(TASK_FILE_NAME));
138
139         handler = fopen(file_name_buf, "r");
140
141         if (!handler) {
142                 _E("can't open %s file\n", file_name_buf);
143                 return;
144         }
145
146         while (read_int(handler, &pid_for_read) >= 0) {
147                 tc.pids = g_array_append_val(tc.pids, pid_for_read);
148                 ++tc.pid_count;
149         }
150         *tasks_classids_list = g_array_append_val(*tasks_classids_list, tc);
151
152         fclose(handler);
153 }
154
155 u_int32_t get_classid_by_app_id(const char *app_id, int create)
156 {
157         char pkgname[MAX_PATH_LENGTH];
158         extract_pkgname(app_id, pkgname, sizeof(pkgname));
159         return get_classid_by_pkg_name(pkgname, create);
160 }
161
162 API u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create)
163 {
164         int ret = 0;
165         int exists;
166         u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
167
168         if (!strcmp(pkg_name, RESOURCED_ALL_APP))
169                 return RESOURCED_ALL_APP_CLASSID;
170
171         if (!strcmp(pkg_name, TETHERING_APP_NAME))
172                 return RESOURCED_TETHERING_APP_CLASSID;
173
174         /* just read */
175         if (!create)
176                 classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR,
177                         pkg_name);
178
179         if (classid != RESOURCED_UNKNOWN_CLASSID)
180                 return classid;
181
182         ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name,
183                 &exists);
184         if (ret)
185                 goto handle_error;
186
187         if (exists)
188                 classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR,
189                         pkg_name);
190         else
191                 ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR,
192                         (char *)pkg_name, &classid);
193         if (ret)
194                 goto handle_error;
195
196         return classid;
197
198  handle_error:
199
200         ETRACE_RET_ERRCODE(ret);
201         return RESOURCED_UNKNOWN_CLASSID;
202 }
203
204
205 int update_classids(void)
206 {
207         DIR *dir;
208         struct dirent *entry;
209
210         char file_name_buf[256];
211         size_t path_to_cgroup_dir_len =
212             sizeof(PATH_TO_NET_CGROUP_DIR), file_name_len;
213
214         sprintf(file_name_buf, "%s/", PATH_TO_NET_CGROUP_DIR);
215
216         if (tasks_classids) {
217                 array_foreach(tc, struct task_classid, tasks_classids) {
218                         g_array_free(tc->pids, TRUE);
219                 }
220                 g_array_free(tasks_classids, TRUE);
221         }
222
223         tasks_classids = g_array_new(FALSE, FALSE, sizeof(struct task_classid));
224
225         dir = opendir(file_name_buf);
226
227         if (!dir)
228                 return ERROR_UPDATE_CLASSIDS_LIST;
229
230         while ((entry = readdir(dir)) != 0) {
231                 if (entry->d_type != DT_DIR || entry->d_name[0] == '.')
232                         continue;
233
234                 file_name_len = strlen(entry->d_name);
235                 if (file_name_len + path_to_cgroup_dir_len >
236                     sizeof(file_name_buf)) {
237                         _E("not enought buffer size\n");
238                         continue;
239                 }
240
241                 strncpy(file_name_buf + path_to_cgroup_dir_len, entry->d_name,
242                         file_name_len + 1);
243
244                 populate_classids_with_pids(file_name_buf,
245                                   path_to_cgroup_dir_len + file_name_len,
246                                   entry->d_name, &tasks_classids);
247         }
248         closedir(dir);
249         _D("class id table updated");
250         return 0;
251 }
252
253 int_array *get_monitored_pids(void)
254 {
255         int_array *result = g_array_new(FALSE, FALSE, sizeof(pid_t));
256
257         if (!result) {
258                 _D("Out of memory\n");
259                 return 0;
260         }
261
262         array_foreach(tc, struct task_classid, tasks_classids) {
263                 int i = 0;
264
265                 for (; i < tc->pid_count; ++i) {
266                         result = g_array_append_val(result,
267                                 g_array_index(tc->pids, int, i));
268                 }
269         }
270         return result;
271 }
272
273 static char *get_app_id_by_classid_local(const u_int32_t classid)
274 {
275         if (classid == RESOURCED_TETHERING_APP_CLASSID)
276                 return strdup(TETHERING_APP_NAME);
277         array_foreach(tc, struct task_classid, tasks_classids)
278                 if (classid == tc->classid)
279                         return strdup(tc->cgroup_name);
280         return 0;
281 }
282
283 char *get_app_id_by_classid(const u_int32_t classid, const bool update_state)
284 {
285         int ret;
286         char *appid = get_app_id_by_classid_local(classid);
287
288         if (appid)
289                 return appid;
290
291         _D("can't resolve app id");
292         if (!update_state)
293                 return 0;
294
295         ret = update_classids();
296         ret_value_msg_if(ret, 0, "Can't get appid for %d", classid);
297
298         return get_app_id_by_classid_local(classid);
299 }
300
301 API resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_name)
302 {
303         int ret = 0;
304         int exists = 0;
305
306         if (pkg_name == NULL) {
307                 _E("package name must be not empty");
308                 return RESOURCED_ERROR_INVALID_PARAMETER;
309         }
310
311         _SD("pkg: %s; pid: %d\n", pkg_name, pid);
312
313         ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name, &exists);
314         ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
315
316         if (!exists) {
317                 ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name,
318                         NULL);
319                 ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
320         }
321
322         return place_pid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid);
323 }
324