tizen 2.3.1 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                 strncpy(tc.cgroup_name, cgroup_name_buf, sizeof(tc.cgroup_name)-1);
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         struct dirent *result;
210         int ret;
211
212         char file_name_buf[256];
213         size_t path_to_cgroup_dir_len =
214             sizeof(PATH_TO_NET_CGROUP_DIR), file_name_len;
215
216         snprintf(file_name_buf, sizeof(file_name_buf), "%s/", PATH_TO_NET_CGROUP_DIR);
217
218         if (tasks_classids) {
219                 array_foreach(tc, struct task_classid, tasks_classids) {
220                         g_array_free(tc->pids, TRUE);
221                 }
222                 g_array_free(tasks_classids, TRUE);
223         }
224
225         tasks_classids = g_array_new(FALSE, FALSE, sizeof(struct task_classid));
226
227         dir = opendir(file_name_buf);
228
229         if (!dir)
230                 return ERROR_UPDATE_CLASSIDS_LIST;
231
232         while (!(ret = readdir_r(dir, &entry, &result)) && result != NULL) {
233                 if (entry.d_type != DT_DIR || entry.d_name[0] == '.')
234                         continue;
235
236                 file_name_len = strlen(entry.d_name);
237                 if (file_name_len + path_to_cgroup_dir_len >
238                     sizeof(file_name_buf)) {
239                         _E("not enought buffer size\n");
240                         continue;
241                 }
242
243                 strncpy(file_name_buf + path_to_cgroup_dir_len, entry.d_name,
244                         file_name_len + 1);
245
246                 populate_classids_with_pids(file_name_buf,
247                                   path_to_cgroup_dir_len + file_name_len,
248                                   entry.d_name, &tasks_classids);
249         }
250         closedir(dir);
251
252         if (ret)
253                 return RESOURCED_ERROR_FAIL;
254
255         _D("class id table updated");
256         return 0;
257 }
258
259 int_array *get_monitored_pids(void)
260 {
261         int_array *result = g_array_new(FALSE, FALSE, sizeof(pid_t));
262
263         if (!result) {
264                 _D("Out of memory\n");
265                 return 0;
266         }
267
268         array_foreach(tc, struct task_classid, tasks_classids) {
269                 int i = 0;
270
271                 for (; i < tc->pid_count; ++i) {
272                         result = g_array_append_val(result,
273                                 g_array_index(tc->pids, int, i));
274                 }
275         }
276         return result;
277 }
278
279 static char *get_app_id_by_classid_local(const u_int32_t classid)
280 {
281         if (classid == RESOURCED_TETHERING_APP_CLASSID)
282                 return strdup(TETHERING_APP_NAME);
283         array_foreach(tc, struct task_classid, tasks_classids)
284                 if (classid == tc->classid)
285                         return strdup(tc->cgroup_name);
286         return 0;
287 }
288
289 char *get_app_id_by_classid(const u_int32_t classid, const bool update_state)
290 {
291         int ret;
292         char *appid = get_app_id_by_classid_local(classid);
293
294         if (appid)
295                 return appid;
296
297         _D("can't resolve app id");
298         if (!update_state)
299                 return 0;
300
301         ret = update_classids();
302         ret_value_msg_if(ret, 0, "Can't get appid for %d", classid);
303
304         return get_app_id_by_classid_local(classid);
305 }
306
307 API resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_name)
308 {
309         int ret = 0;
310         int exists = 0;
311
312         if (pkg_name == NULL) {
313                 _E("package name must be not empty");
314                 return RESOURCED_ERROR_INVALID_PARAMETER;
315         }
316
317         _SD("pkg: %s; pid: %d\n", pkg_name, pid);
318
319         ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name, &exists);
320         ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
321
322         if (!exists) {
323                 ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name,
324                         NULL);
325                 ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
326         }
327
328         return place_pid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid);
329 }
330