Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-License-Identifier: LGPL-2.1-or-later | ||
2 | // Copyright (C) 2024 Omar Castro | ||
3 | #include "glib-object.h" | ||
4 | #include "glib.h" | ||
5 | #include "glibconfig.h" | ||
6 | #include <stdbool.h> | ||
7 | #define _GNU_SOURCE | ||
8 | #include <grp.h> | ||
9 | #include <pwd.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <stdio.h> | ||
12 | |||
13 | #include <unistd.h> | ||
14 | #include <string.h> | ||
15 | #include <gmodule.h> | ||
16 | #include <time.h> | ||
17 | #include <fcntl.h> | ||
18 | #include "logger.h" | ||
19 | #include "app.h" | ||
20 | #include "json-glib.extension.h" | ||
21 | #include "accepted-actions.enum.h" | ||
22 | #include "error-message.dialog.h" | ||
23 | #include "polkit-auth-handler.service.h" | ||
24 | #include "request-messages.h" | ||
25 | |||
26 |
6/7✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 8 times.
|
40 | G_DEFINE_TYPE(CmdPkAgentPolkitListener, cmd_pk_agent_polkit_listener, POLKIT_AGENT_TYPE_LISTENER) |
27 | |||
28 | typedef enum { | ||
29 | IN_QUEUE, | ||
30 | AUTHENTICATING, | ||
31 | CANCELED, | ||
32 | AUTHORIZED | ||
33 | } AuthDlgDataStatus; | ||
34 | |||
35 | typedef struct _AuthDlgData AuthDlgData; | ||
36 | struct _AuthDlgData { | ||
37 | PolkitAgentSession *session; | ||
38 | PolkitActionDescription* action_description; | ||
39 | gchar *action_id; | ||
40 | gchar *cookie; | ||
41 | gchar *message; | ||
42 | GTask* task; | ||
43 | GList *identities; | ||
44 | GError *error; | ||
45 | |||
46 | AuthDlgDataStatus status; | ||
47 | |||
48 | GPid cmd_pid; | ||
49 | int write_channel_fd; | ||
50 | int read_channel_fd; | ||
51 | guint read_channel_watcher; | ||
52 | |||
53 | GIOChannel * write_channel; | ||
54 | GIOChannel * read_channel; | ||
55 | GString * buffer; | ||
56 | GString * active_line; | ||
57 | |||
58 | JsonParser *parser; | ||
59 | JsonObject *root; | ||
60 | |||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * Authentication queue to be used in serial mode | ||
65 | * The an authentication goes to the queur if there is one authentication currently being handled | ||
66 | */ | ||
67 | GAsyncQueue * serial_mode_queue = NULL; | ||
68 | AuthDlgData * serial_mode_current_authentication = NULL; | ||
69 | |||
70 | 3 | bool serie_mode_is_queue_empty(){ | |
71 |
3/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
|
3 | return serial_mode_queue == NULL || g_async_queue_length(serial_mode_queue) <= 0; |
72 | } | ||
73 | |||
74 | 2 | void serie_mode_push_auth_to_queue(AuthDlgData *d){ | |
75 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if(serial_mode_queue == NULL){ |
76 | 1 | serial_mode_queue = g_async_queue_new(); | |
77 | } | ||
78 | 2 | g_async_queue_push(serial_mode_queue, d); | |
79 | 2 | } | |
80 | |||
81 | 2 | AuthDlgData* serie_mode_pop_auth_from_queue(){ | |
82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if(serial_mode_queue == NULL){ |
83 | ✗ | serial_mode_queue = g_async_queue_new(); | |
84 | } | ||
85 | 2 | return (AuthDlgData *) g_async_queue_pop(serial_mode_queue); | |
86 | } | ||
87 | |||
88 | static void build_session(AuthDlgData *d); | ||
89 | static void spawn_command_for_authentication(AuthDlgData *d); | ||
90 | |||
91 | 23 | void auth_dialog_data_write_to_channel ( AuthDlgData *data, const char * message){ | |
92 | 23 | GIOChannel * write_channel = data->write_channel; | |
93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if(data->write_channel == NULL){ |
94 | //gets here when the script exits or there was an error loading it | ||
95 | ✗ | return; | |
96 | } | ||
97 | 23 | log__verbose__writing_to_command_stdin(message); | |
98 | gsize bytes_witten; | ||
99 | 23 | g_io_channel_write_chars(write_channel, message, -1, &bytes_witten, &data->error); | |
100 | 23 | g_io_channel_write_unichar(write_channel, '\n', &data->error); | |
101 | 23 | g_io_channel_flush(write_channel, &data->error); | |
102 | } | ||
103 | |||
104 | 18 | static void auth_dlg_data_run_and_free_task(AuthDlgData *d){ | |
105 | 18 | GTask *task = d->task; | |
106 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
|
18 | if(task != NULL){ |
107 | 9 | g_task_return_boolean(task, true); | |
108 | 9 | g_object_unref(task); | |
109 | 9 | d->task = NULL; | |
110 | } | ||
111 | 18 | } | |
112 | |||
113 | 9 | static void auth_dlg_data_free(AuthDlgData *d) | |
114 | { | ||
115 | 9 | GError* error = NULL; | |
116 | |||
117 | 9 | auth_dlg_data_run_and_free_task(d); | |
118 | 9 | g_object_unref(d->session); | |
119 | 9 | g_free(d->action_id); | |
120 | 9 | g_free(d->cookie); | |
121 | 9 | g_free(d->message); | |
122 | 9 | g_list_free(d->identities); | |
123 | 9 | g_source_remove (d->read_channel_watcher); | |
124 | 9 | g_io_channel_shutdown(d->write_channel, TRUE, &error); | |
125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if(error){ |
126 | ✗ | fprintf(stderr, "error closing write channel of pid %d: %s\n", d->cmd_pid, error->message); | |
127 | ✗ | g_error_free ( error ); | |
128 | } | ||
129 | 9 | g_io_channel_unref(d->write_channel); | |
130 | 9 | g_io_channel_shutdown(d->read_channel, FALSE, &error); | |
131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if(error){ |
132 | ✗ | fprintf(stderr, "error closing read channel of pid %d: %s\n", d->cmd_pid, error->message); | |
133 | ✗ | g_error_free ( error ); | |
134 | } | ||
135 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if(d->action_description != NULL){ |
136 | 9 | g_object_unref(d->action_description); | |
137 | } | ||
138 | 9 | g_io_channel_unref(d->read_channel); | |
139 | 9 | g_string_free(d->active_line, true); | |
140 | 9 | g_string_free(d->buffer, true); | |
141 | 9 | g_object_unref(d->parser); | |
142 | 9 | g_slice_free(AuthDlgData, d); | |
143 | 9 | } | |
144 | |||
145 | 12 | static gboolean on_new_input ( GIOChannel *source, [[maybe_unused]] GIOCondition condition, gpointer context ) | |
146 | { | ||
147 | 12 | log__verbose__reading_command_stdout(); | |
148 | 12 | AuthDlgData *data = (AuthDlgData *) context; | |
149 | 12 | GString * buffer = data->buffer; | |
150 | 12 | GString * active_line = data->active_line; | |
151 | |||
152 | 12 | gboolean newline = FALSE; | |
153 | |||
154 | 12 | GError * error = NULL; | |
155 | gunichar unichar; | ||
156 | GIOStatus status; | ||
157 | |||
158 | 12 | status = g_io_channel_read_unichar(source, &unichar, &error); | |
159 | |||
160 | //when there is nothing to read, status is G_IO_STATUS_AGAIN | ||
161 |
2/2✓ Branch 0 taken 539 times.
✓ Branch 1 taken 12 times.
|
551 | while(status == G_IO_STATUS_NORMAL) { |
162 | 539 | g_string_append_unichar(buffer, unichar); | |
163 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 527 times.
|
539 | if( unichar == '\n' ){ |
164 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if(buffer->len > 1){ //input is not an empty line |
165 | 12 | g_debug("received new line: %s", buffer->str); | |
166 | 12 | g_string_assign(active_line, buffer->str); | |
167 | 12 | newline=TRUE; | |
168 | } | ||
169 | 12 | log__verbose__received_from_command_stdout(buffer->str); | |
170 | 12 | g_string_set_size(buffer, 0); | |
171 | } | ||
172 | 539 | status = g_io_channel_read_unichar(source, &unichar, &error); | |
173 | } | ||
174 | |||
175 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if(newline){ |
176 | 12 | fprintf(stderr, "parsing line\n"); | |
177 | |||
178 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | if(! json_parser_load_from_data(data->parser,data->active_line->str,data->active_line->len,&error)){ |
179 | ✗ | fprintf(stderr, "Unable to parse line: %s\n", error->message); | |
180 | ✗ | g_error_free ( error ); | |
181 | } else { | ||
182 | |||
183 | 12 | data->root = json_node_get_object(json_parser_get_root(data->parser)); | |
184 | 12 | const char * action = json_object_get_string_member_or_else(data->root, "action", NULL); | |
185 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if(action == NULL){ |
186 | ✗ | fprintf(stderr, "no action defined, ignored"); | |
187 |
2/3✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
12 | } else switch (accepted_action_value_of_str(action)) { |
188 | 1 | case AcceptedAction_CANCEL: { | |
189 | 1 | fprintf(stderr, "action cancel"); | |
190 | 1 | data->status = CANCELED; | |
191 | 1 | polkit_agent_session_cancel(data->session); | |
192 | } | ||
193 | 1 | break; | |
194 | 11 | case AcceptedAction_AUTHENTICATE: { | |
195 | 11 | fprintf(stderr, "action authenticate"); | |
196 | 11 | const char * password = json_object_get_string_member_or_else(data->root, "password", NULL); | |
197 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if(password != NULL){ |
198 | 11 | polkit_agent_session_response(data->session, password); | |
199 | } | ||
200 | } | ||
201 | 11 | break; | |
202 | ✗ | default: | |
203 | ✗ | fprintf(stderr, "unknown action %s \n", action); | |
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | 12 | return G_SOURCE_CONTINUE; | |
209 | } | ||
210 | |||
211 | 12 | static void on_session_completed([[maybe_unused]] PolkitAgentSession* session, gboolean authorized, AuthDlgData* d) | |
212 | { | ||
213 | 12 | bool canceled = d->status == CANCELED; | |
214 | 12 | log__verbose__polkit_session_completed(authorized, canceled); | |
215 | |||
216 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | if(authorized){ |
217 | 8 | d->status = AUTHORIZED; | |
218 | 16 | g_autofree const char* message = request_message_authorization_authorized(); | |
219 | 8 | auth_dialog_data_write_to_channel(d, message); | |
220 | } | ||
221 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
|
12 | if (authorized || canceled) { |
222 | 9 | auth_dlg_data_run_and_free_task(d); | |
223 | 9 | auth_dlg_data_free(d); | |
224 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
|
9 | if(app__get_auth_handling_mode() == AuthHandlingMode_SERIE){ |
225 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
|
3 | if(serie_mode_is_queue_empty()){ |
226 | 1 | serial_mode_current_authentication = NULL; | |
227 | } else { | ||
228 | 2 | AuthDlgData* data = serie_mode_pop_auth_from_queue(); | |
229 | 2 | data->status = AUTHENTICATING; | |
230 | 2 | serial_mode_current_authentication = data; | |
231 | 2 | spawn_command_for_authentication(data); | |
232 | 2 | polkit_agent_session_initiate(data->session); | |
233 | } | ||
234 | } | ||
235 | |||
236 | 9 | return; | |
237 | } | ||
238 | 3 | g_object_unref(d->session); | |
239 | 3 | d->session = NULL; | |
240 | 6 | g_autofree const char* message = request_message_authorization_not_authorized(); | |
241 | 3 | auth_dialog_data_write_to_channel(d, message); | |
242 | 3 | build_session(d); | |
243 | 3 | polkit_agent_session_initiate(d->session); | |
244 | |||
245 | } | ||
246 | |||
247 | 12 | static void on_session_request([[maybe_unused]] PolkitAgentSession* session, gchar *req, gboolean visibility, AuthDlgData *d) | |
248 | { | ||
249 | 12 | log__verbose__polkit_session_request(req, visibility); | |
250 | 24 | g_autofree const char *write_message = request_message_request_password(req, d->message, d->action_description); | |
251 | 12 | auth_dialog_data_write_to_channel(d, write_message); | |
252 | 12 | } | |
253 | |||
254 | ✗ | static void on_session_show_error([[maybe_unused]] PolkitAgentSession* session, gchar *text, [[maybe_unused]] AuthDlgData* d) | |
255 | { | ||
256 | |||
257 | ✗ | log__verbose__polkit_session_show_error(text); | |
258 | ✗ | } | |
259 | |||
260 | ✗ | static void on_session_show_info([[maybe_unused]] PolkitAgentSession *session, gchar *text, [[maybe_unused]] AuthDlgData* d) | |
261 | { | ||
262 | ✗ | log__verbose__polkit_session_show_info(text); | |
263 | ✗ | } | |
264 | |||
265 | 12 | static void build_session(AuthDlgData *d){ | |
266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (G_UNLIKELY(d->session)) { |
267 | ✗ | g_signal_handlers_disconnect_matched(d->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, d); | |
268 | ✗ | polkit_agent_session_cancel(d->session); | |
269 | ✗ | g_object_unref(d->session); | |
270 | } | ||
271 | |||
272 | 12 | PolkitIdentity *id = (PolkitIdentity *)d->identities->data; | |
273 | 12 | d->session = polkit_agent_session_new(id, d->cookie); | |
274 | 12 | g_signal_connect(d->session, "completed", G_CALLBACK(on_session_completed), d); | |
275 | 12 | g_signal_connect(d->session, "request", G_CALLBACK(on_session_request), d); | |
276 | 12 | g_signal_connect(d->session, "show-error", G_CALLBACK(on_session_show_error), d); | |
277 | 12 | g_signal_connect(d->session, "show-info", G_CALLBACK(on_session_show_info), d); | |
278 | |||
279 | 12 | } | |
280 | |||
281 | 9 | static void spawn_command_for_authentication(AuthDlgData *d){ | |
282 | 9 | GError *error = NULL; | |
283 | int cmd_input_fd; | ||
284 | int cmd_output_fd; | ||
285 | |||
286 | 9 | char ** const cmd_argv = app__get_cmd_line_argv(); | |
287 | |||
288 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if ( ! g_spawn_async_with_pipes ( NULL, cmd_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &(d->cmd_pid), &(cmd_input_fd), &(cmd_output_fd), NULL, &error)) { |
289 | ✗ | show_error_message_format("%s", error->message); | |
290 | ✗ | polkit_agent_session_cancel(d->session); | |
291 | ✗ | return; | |
292 | } | ||
293 | 9 | d->read_channel_fd = cmd_output_fd; | |
294 | 9 | d->write_channel_fd = cmd_input_fd; | |
295 | |||
296 | 9 | int retval = fcntl( d->read_channel_fd, F_SETFL, fcntl(d->read_channel_fd, F_GETFL) | O_NONBLOCK); | |
297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (retval != 0){ |
298 | ✗ | fprintf(stderr,"Error setting non block on output pipe\n"); | |
299 | ✗ | kill(d->cmd_pid, SIGTERM); | |
300 | ✗ | polkit_agent_session_cancel(d->session); | |
301 | ✗ | return; | |
302 | } | ||
303 | |||
304 | 9 | d->read_channel = g_io_channel_unix_new(d->read_channel_fd); | |
305 | 9 | d->write_channel = g_io_channel_unix_new(d->write_channel_fd); | |
306 | 9 | d->read_channel_watcher = g_io_add_watch(d->read_channel, G_IO_IN, on_new_input, d); | |
307 | } | ||
308 | |||
309 | |||
310 | /** | ||
311 | * Authentication request handler of PolkitAgentListener. | ||
312 | * | ||
313 | */ | ||
314 | 9 | static void initiate_authentication(PolkitAgentListener *listener, | |
315 | const gchar *action_id, | ||
316 | const gchar *message, | ||
317 | const gchar *icon_name, | ||
318 | PolkitDetails *details, | ||
319 | const gchar *cookie, | ||
320 | GList *identities, | ||
321 | GCancellable *cancellable, | ||
322 | GAsyncReadyCallback callback, | ||
323 | gpointer user_data) | ||
324 | { | ||
325 | |||
326 | 9 | log__verbose__init_polkit_authentication(action_id, message, icon_name, cookie); | |
327 | 9 | log__verbose__polkit_auth_identities(identities); | |
328 | 9 | log__verbose__polkit_auth_details(details); | |
329 | |||
330 | 9 | AuthDlgData *d = g_slice_new0(AuthDlgData); | |
331 | |||
332 | 9 | GError *error = NULL; | |
333 | 9 | PolkitAuthority* authority = polkit_authority_get_sync(NULL, &error); | |
334 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if(error == NULL){ |
335 | 9 | GList* actions = polkit_authority_enumerate_actions_sync (authority,NULL,&error); | |
336 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if(error == NULL){ |
337 |
2/2✓ Branch 0 taken 1773 times.
✓ Branch 1 taken 9 times.
|
1782 | for(GList *elem = actions; elem; elem = elem->next) { |
338 | 1773 | PolkitActionDescription* action_description = elem->data; | |
339 |
2/2✓ Branch 0 taken 738 times.
✓ Branch 1 taken 1035 times.
|
1773 | if(d->action_description != NULL){ |
340 | // continue to g_object_unref the remaining elements on the list, as they are required | ||
341 | // before freeing the `actions` GList | ||
342 | 738 | g_object_unref(action_description); | |
343 | 738 | continue; | |
344 | } | ||
345 | |||
346 | 1035 | const gchar * action_description_action_id = polkit_action_description_get_action_id(action_description); | |
347 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1026 times.
|
1035 | if(strcmp(action_description_action_id, action_id) == 0){ |
348 | 9 | log__verbose__polkit_action_description(action_description); | |
349 | 9 | g_object_ref(action_description); | |
350 | 9 | d->action_description = action_description; | |
351 | } | ||
352 | |||
353 | 1035 | g_object_unref(action_description); | |
354 | } | ||
355 | 9 | g_list_free(actions); | |
356 | } | ||
357 | 9 | g_object_unref(authority); | |
358 | } | ||
359 | |||
360 | 9 | d->task = g_task_new(listener, cancellable, callback, user_data); | |
361 | 9 | d->action_id = g_strdup(action_id); | |
362 | 9 | d->message = g_strdup(message); | |
363 | 9 | d->cookie = g_strdup(cookie); | |
364 | 9 | d->identities = g_list_copy(identities); | |
365 | 9 | d->buffer = g_string_sized_new (1024); | |
366 | 9 | d->active_line = g_string_sized_new (1024); | |
367 | 9 | d->parser = json_parser_new (); | |
368 | 9 | build_session(d); | |
369 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
|
9 | if(app__get_auth_handling_mode() == AuthHandlingMode_PARALLEL){ |
370 | 6 | spawn_command_for_authentication(d); | |
371 | 6 | polkit_agent_session_initiate(d->session); | |
372 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | } else if(serial_mode_current_authentication != NULL){ |
373 | 2 | d->status = IN_QUEUE; | |
374 | 2 | serie_mode_push_auth_to_queue(d); | |
375 | } else { | ||
376 | 1 | d->status = AUTHENTICATING; | |
377 | 1 | serial_mode_current_authentication = d; | |
378 | 1 | spawn_command_for_authentication(d); | |
379 | 1 | polkit_agent_session_initiate(d->session); | |
380 | } | ||
381 | 9 | } | |
382 | |||
383 | 9 | static gboolean initiate_authentication_finish( | |
384 | [[maybe_unused]] PolkitAgentListener *listener, | ||
385 | GAsyncResult *res, | ||
386 | GError **error) | ||
387 | { | ||
388 | 9 | log__verbose__finish_polkit_authentication(); | |
389 | 9 | return g_task_propagate_boolean(G_TASK(res), error); | |
390 | } | ||
391 | |||
392 | 6 | static void cmd_pk_agent_polkit_listener_finalize(GObject *object) | |
393 | { | ||
394 | 6 | log__verbose__finalize_polkit_listener(); | |
395 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | g_return_if_fail(object != NULL); |
396 |
4/8✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
|
6 | g_return_if_fail(CMD_PK_AGENT_IS_POLKIT_LISTENER(object)); |
397 | 6 | G_OBJECT_CLASS(cmd_pk_agent_polkit_listener_parent_class)->finalize(object); | |
398 | } | ||
399 | |||
400 | 4 | static void cmd_pk_agent_polkit_listener_class_init(CmdPkAgentPolkitListenerClass *klass) | |
401 | { | ||
402 | 4 | log__verbose__init_polkit_listener(); | |
403 | GObjectClass *g_object_class; | ||
404 | PolkitAgentListenerClass* pkal_class; | ||
405 | 4 | g_object_class = G_OBJECT_CLASS(klass); | |
406 | 4 | g_object_class->finalize = cmd_pk_agent_polkit_listener_finalize; | |
407 | |||
408 | 4 | pkal_class = POLKIT_AGENT_LISTENER_CLASS(klass); | |
409 | 4 | pkal_class->initiate_authentication = initiate_authentication; | |
410 | 4 | pkal_class->initiate_authentication_finish = initiate_authentication_finish; | |
411 | 4 | } | |
412 | |||
413 | 6 | static void cmd_pk_agent_polkit_listener_init([[maybe_unused]] CmdPkAgentPolkitListener *self) | |
414 | { | ||
415 | 6 | } | |
416 | |||
417 | 6 | PolkitAgentListener* cmd_pk_agent_polkit_listener_new(void) | |
418 | { | ||
419 | 6 | return g_object_new(CMD_PK_AGENT_TYPE_POLKIT_LISTENER, NULL); | |
420 | } | ||
421 | |||
422 | |||
423 |