GCC Code Coverage Report


Directory: ./
File: panels/applications/utils.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 106 0.0%
Functions: 0 13 0.0%
Branches: 0 130 0.0%

Line Branch Exec Source
1 /* utils.c
2 *
3 * Copyright 2018 Matthias Clasen <matthias.clasen@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #ifndef _XOPEN_SOURCE
22 #define _XOPEN_SOURCE 600
23 #endif
24
25 #include <config.h>
26 #include <glib/gi18n.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <ftw.h>
31
32 #include "utils.h"
33 #ifdef HAVE_SNAP
34 #include "cc-snapd-client.h"
35 #endif
36
37 static gint
38 ftw_remove_cb (const gchar *path,
39 const struct stat *sb,
40 gint typeflags,
41 struct FTW *ftwbuf)
42 {
43 remove (path);
44 return 0;
45 }
46
47 static void
48 file_remove_thread_func (GTask *task,
49 gpointer source_object,
50 gpointer task_data,
51 GCancellable *cancellable)
52 {
53 GFile *file = source_object;
54 g_autofree gchar *path = g_file_get_path (file);
55
56 nftw (path, ftw_remove_cb, 20, FTW_PHYS | FTW_DEPTH);
57
58 if (g_task_set_return_on_cancel (task, FALSE))
59 g_task_return_boolean (task, TRUE);
60 }
61
62 void
63 file_remove_async (GFile *file,
64 GCancellable *cancellable,
65 GAsyncReadyCallback callback,
66 gpointer data)
67 {
68 g_autoptr(GTask) task = g_task_new (file, cancellable, callback, data);
69 g_task_set_return_on_cancel (task, TRUE);
70 g_task_run_in_thread (task, file_remove_thread_func);
71 }
72
73 gboolean
74 file_remove_finish (GFile *file,
75 GAsyncResult *result,
76 GError **error)
77 {
78 g_return_val_if_fail (g_task_is_valid (result, file), FALSE);
79 return g_task_propagate_boolean (G_TASK (result), error);
80 }
81
82 static GPrivate size_key = G_PRIVATE_INIT (g_free);
83
84 static gint
85 ftw_size_cb (const gchar *path,
86 const struct stat *sb,
87 gint typeflags,
88 struct FTW *ftwbuf)
89 {
90 guint64 *size = (guint64*)g_private_get (&size_key);
91 if (typeflags == FTW_F)
92 *size += sb->st_size;
93 return 0;
94 }
95
96 static void
97 file_size_thread_func (GTask *task,
98 gpointer source_object,
99 gpointer task_data,
100 GCancellable *cancellable)
101 {
102 GFile *file = source_object;
103 g_autofree gchar *path = g_file_get_path (file);
104 guint64 *total;
105
106 g_private_replace (&size_key, g_new0 (guint64, 1));
107
108 nftw (path, ftw_size_cb, 20, FTW_PHYS | FTW_DEPTH);
109
110 total = g_new0 (guint64, 1);
111 *total = *(guint64*)g_private_get (&size_key);
112
113 if (g_task_set_return_on_cancel (task, FALSE))
114 g_task_return_pointer (task, total, g_free);
115 }
116
117 void
118 file_size_async (GFile *file,
119 GCancellable *cancellable,
120 GAsyncReadyCallback callback,
121 gpointer data)
122 {
123 g_autoptr(GTask) task = g_task_new (file, cancellable, callback, data);
124 g_task_set_return_on_cancel (task, TRUE);
125 g_task_run_in_thread (task, file_size_thread_func);
126 }
127
128 gboolean
129 file_size_finish (GFile *file,
130 GAsyncResult *result,
131 guint64 *size,
132 GError **error)
133 {
134 g_autofree guint64 *data = NULL;
135
136 g_return_val_if_fail (g_task_is_valid (result, file), FALSE);
137 data = g_task_propagate_pointer (G_TASK (result), error);
138 if (data == NULL)
139 return FALSE;
140 if (size != NULL)
141 *size = *data;
142 return TRUE;
143 }
144
145 static gchar *
146 get_output_of (const gchar **argv)
147 {
148 g_autofree gchar *output = NULL;
149 int status;
150
151 if (!g_spawn_sync (NULL,
152 (gchar**) argv,
153 NULL,
154 G_SPAWN_SEARCH_PATH,
155 NULL, NULL,
156 &output, NULL,
157 &status, NULL))
158 return NULL;
159
160 if (!g_spawn_check_wait_status (status, NULL))
161 return NULL;
162
163 return g_steal_pointer (&output);
164 }
165
166 GKeyFile *
167 get_flatpak_metadata (const gchar *app_id)
168 {
169 const gchar *argv[5] = { "flatpak", "info", "-m", "app", NULL };
170 g_autofree gchar *data = NULL;
171 g_autoptr(GError) error = NULL;
172 g_autoptr(GKeyFile) keyfile = NULL;
173
174 argv[3] = app_id;
175
176 data = get_output_of (argv);
177 if (data == NULL)
178 return NULL;
179
180 keyfile = g_key_file_new ();
181 if (!g_key_file_load_from_data (keyfile, data, -1, 0, &error))
182 {
183 g_warning ("%s", error->message);
184 return NULL;
185 }
186
187 return g_steal_pointer (&keyfile);
188 }
189
190 guint64
191 get_flatpak_app_size (const gchar *app_id)
192 {
193 const gchar *argv[5] = { "flatpak", "info", "-s", "app", NULL };
194 g_autofree gchar *data = NULL;
195 guint64 factor;
196 double val;
197
198 argv[3] = app_id;
199
200 data = get_output_of (argv);
201 if (data == NULL)
202 return 0;
203
204 data = g_strstrip (data);
205
206 if (g_str_has_suffix (data, "kB") || g_str_has_suffix (data, "kb"))
207 factor = 1000;
208 else if (g_str_has_suffix (data, "MB") || g_str_has_suffix (data, "Mb"))
209 factor = 1000 * 1000;
210 else if (g_str_has_suffix (data, "GB") || g_str_has_suffix (data, "Gb"))
211 factor = 1000 * 1000 * 1000;
212 else if (g_str_has_suffix (data, "KiB") || g_str_has_suffix (data, "Kib"))
213 factor = 1024;
214 else if (g_str_has_suffix (data, "MiB") || g_str_has_suffix (data, "Mib"))
215 factor = 1024 * 1024;
216 else if (g_str_has_suffix (data, "GiB") || g_str_has_suffix (data, "Gib"))
217 factor = 1024 * 1024 * 1024;
218 else
219 factor = 1;
220
221 val = g_ascii_strtod (data, NULL);
222
223 return (guint64)(val * factor);
224 }
225
226 guint64
227 get_snap_app_size (const gchar *snap_name)
228 {
229 #ifdef HAVE_SNAP
230 g_autoptr(CcSnapdClient) client = NULL;
231 g_autoptr(JsonObject) snap = NULL;
232 g_autoptr(GError) error = NULL;
233
234 client = cc_snapd_client_new ();
235 snap = cc_snapd_client_get_snap_sync (client, snap_name, NULL, &error);
236 if (snap == NULL)
237 {
238 g_warning ("Failed to get snap size: %s", error->message);
239 return 0;
240 }
241
242 return json_object_get_int_member (snap, "installed-size");
243 #else
244 return 0;
245 #endif
246 }
247
248 char *
249 get_app_id (GAppInfo *info)
250 {
251 gchar *app_id = g_strdup (g_app_info_get_id (info));
252
253 if (g_str_has_suffix (app_id, ".desktop"))
254 app_id[strlen (app_id) - strlen (".desktop")] = '\0';
255
256 return app_id;
257 }
258