1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of tlm (Tiny Login Manager)
6 * Copyright (C) 2013 Intel Corporation.
8 * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9 * Jussi Laako <jussi.laako@linux.intel.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27 #include <sys/types.h>
30 #include <glib/gstdio.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/inotify.h>
40 #include <glib/gstdio.h>
41 #include <glib-unix.h>
44 #include "tlm-utils.h"
47 #define HOST_NAME_SIZE 256
50 g_clear_string (gchar **str)
59 tlm_user_get_name (uid_t user_id)
63 pwent = getpwuid (user_id);
67 return pwent->pw_name;
71 tlm_user_get_uid (const gchar *username)
75 pwent = getpwnam (username);
83 tlm_user_get_gid (const gchar *username)
87 pwent = getpwnam (username);
95 tlm_user_get_home_dir (const gchar *username)
99 pwent = getpwnam (username);
103 return pwent->pw_dir;
107 tlm_user_get_shell (const gchar *username)
109 struct passwd *pwent;
111 pwent = getpwnam (username);
115 return pwent->pw_shell;
119 tlm_utils_delete_dir (
125 if (!dir || !(gdir = g_dir_open(dir, 0, NULL))) {
129 const gchar *fname = NULL;
131 gchar *filepath = NULL;
132 while ((fname = g_dir_read_name (gdir)) != NULL) {
133 if (g_strcmp0 (fname, ".") == 0 ||
134 g_strcmp0 (fname, "..") == 0) {
138 filepath = g_build_filename (dir, fname, NULL);
140 retval = lstat(filepath, &sent);
142 /* recurse the directory */
143 if (S_ISDIR (sent.st_mode)) {
144 retval = (gint)!tlm_utils_delete_dir (filepath);
146 retval = g_remove (filepath);
158 if (g_remove (dir) != 0) {
167 const gchar *tty_name)
170 const gchar *tmp = tty_name;
173 if (isdigit (*tmp)) {
184 const gchar *hostname)
186 gchar *hostaddress = NULL;
187 struct addrinfo hints, *info = NULL;
189 if (!hostname) return NULL;
191 memset (&hints, 0, sizeof (hints));
192 hints.ai_flags = AI_ADDRCONFIG;
194 if (getaddrinfo (hostname, NULL, &hints, &info) == 0) {
196 if (info->ai_family == AF_INET) {
197 struct sockaddr_in *sa = (struct sockaddr_in *) info->ai_addr;
198 hostaddress = g_malloc0 (sizeof(struct in_addr));
199 memcpy (hostaddress, &(sa->sin_addr), sizeof (struct in_addr));
200 } else if (info->ai_family == AF_INET6) {
201 struct sockaddr_in6 *sa = (struct sockaddr_in6 *) info->ai_addr;
202 hostaddress = g_malloc0 (sizeof(struct in6_addr));
203 memcpy (hostaddress, &(sa->sin6_addr),
204 sizeof (struct in6_addr));
214 const gchar *tty1_name,
215 const gchar *tty2_name)
217 gchar *tty1 = NULL, *tty2 = NULL;
218 gboolean res = FALSE;
220 if (tty1_name == tty2_name) return TRUE;
221 if (!tty1_name || !tty2_name) return FALSE;
223 if (*tty1_name == '/') tty1 = g_strdup (tty1_name);
224 else tty1 = g_strdup_printf ("/dev/%s", tty1_name);
225 if (*tty2_name == '/') tty2 = g_strdup (tty2_name);
226 else tty2 = g_strdup_printf ("/dev/%s", tty2_name);
228 res = (g_strcmp0 (tty1_name, tty2_name) == 0);
238 gchar *name = g_malloc0 (HOST_NAME_SIZE);
239 if (gethostname (name, HOST_NAME_SIZE) != 0) {
247 tlm_utils_log_utmp_entry (const gchar *username)
252 struct utmp *ut_tmp = NULL;
253 gchar *hostname = NULL, *hostaddress = NULL;
254 const gchar *tty_name = NULL;
255 gchar *tty_no_dev_name = NULL, *tty_id = NULL;
257 DBG ("Log session entry to utmp/wtmp");
259 hostname = _get_host_name ();
260 hostaddress = _get_host_address (hostname);
261 tty_name = ttyname (0);
263 tty_no_dev_name = g_strdup (strncmp(tty_name, "/dev/", 5) == 0 ?
264 tty_name + 5 : tty_name);
266 tty_id = _get_tty_id (tty_no_dev_name);
268 utmpname (_PATH_UTMP);
271 while ((ut_tmp = getutent())) {
272 if ( (ut_tmp->ut_pid == pid) &&
273 (ut_tmp->ut_id[0] != '\0') &&
274 (ut_tmp->ut_type == LOGIN_PROCESS ||
275 ut_tmp->ut_type == USER_PROCESS) &&
276 (_is_tty_same (ut_tmp->ut_line, tty_name))) {
281 if (ut_tmp) memcpy (&ut_ent, ut_tmp, sizeof (ut_ent));
282 else memset (&ut_ent, 0, sizeof (ut_ent));
284 ut_ent.ut_type = USER_PROCESS;
287 strncpy (ut_ent.ut_id, tty_id, sizeof (ut_ent.ut_id));
289 strncpy (ut_ent.ut_user, username, sizeof (ut_ent.ut_user));
291 strncpy (ut_ent.ut_line, tty_no_dev_name, sizeof (ut_ent.ut_line));
293 strncpy (ut_ent.ut_host, hostname, sizeof (ut_ent.ut_host));
295 memcpy (&ut_ent.ut_addr_v6, hostaddress, sizeof (ut_ent.ut_addr_v6));
297 ut_ent.ut_session = getsid (0);
298 gettimeofday (&tv, NULL);
300 ut_ent.ut_tv.tv_sec = tv.tv_sec;
301 ut_ent.ut_tv.tv_usec = tv.tv_usec;
303 ut_ent.ut_time = tv.tv_sec;
309 updwtmp (_PATH_WTMP, &ut_ent);
311 g_free (hostaddress);
313 g_free (tty_no_dev_name);
318 _split_command_line_with_regex(const char *command, GRegex *regex) {
319 gchar **temp_strv = NULL;
320 gchar **temp_iter = NULL, **args_iter = NULL;
323 temp_strv = regex ? g_regex_split (regex, command, G_REGEX_MATCH_NOTEMPTY)
324 : g_strsplit (command, " ", -1);
326 WARN("Failed to split command: %s", command);
330 argv = g_new0 (gchar *, g_strv_length (temp_strv));
331 for (temp_iter = temp_strv, args_iter = argv;
335 gchar *item = g_strstrip (*temp_iter);
337 item_len = strlen (item);
341 if ((item[0] == '\"' && item[item_len - 1] == '\"') ||
342 (item[0] == '\'' && item[item_len - 1] == '\'')) {
343 item[item_len - 1] = '\0';
344 memmove (item, item + 1, item_len - 1);
346 *args_iter = g_strcompress (item);
349 g_strfreev (temp_strv);
355 tlm_utils_split_command_line(const gchar *command) {
356 const gchar *pattern = "('.*?'|\".*?\"|\\S+)";
357 GError *error = NULL;
358 GRegex *regex = NULL;
362 WARN("Cannot pase NULL arguments string");
366 if (!(regex = g_regex_new(pattern, 0, G_REGEX_MATCH_NOTEMPTY, &error))) {
367 WARN("Failed to create regex: %s", error->message);
371 argv = _split_command_line_with_regex (command, regex);
373 g_regex_unref (regex);
379 tlm_utils_split_command_lines (const GList const *commands_list) {
380 const gchar *pattern = "('.*?'|\".*?\"|\\S+)";
381 GError *error = NULL;
382 GRegex *regex = NULL;
383 GList *argv_list = NULL;
384 const GList *tmp_list = NULL;
386 if (!commands_list) {
390 if (!(regex = g_regex_new(pattern, 0, G_REGEX_MATCH_NOTEMPTY, &error))) {
391 WARN("Failed to create regex: %s", error->message);
395 for (tmp_list = commands_list; tmp_list; tmp_list = tmp_list->next) {
396 argv_list = g_list_append (argv_list, _split_command_line_with_regex (
397 (const gchar *)tmp_list->data, regex));
400 g_regex_unref (regex);
407 GHashTable *dir_table; /* { gchar*: GList* } */
408 GHashTable *wd_table; /* { int: const gchar* } */
419 WatchInfo *info = g_slice_new0 (WatchInfo);
422 info->userdata = userdata;
423 info->dir_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
424 info->wd_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
430 _destroy_dir_table_entry (gpointer key, gpointer value, gpointer userdata) {
433 g_list_free_full ((GList *)value, g_free);
437 _destroy_wd_table_entry (gpointer key, gpointer value, gpointer userdata) {
439 int ifd = GPOINTER_TO_INT(userdata);
440 int wd = GPOINTER_TO_INT(key);
442 inotify_rm_watch (ifd, wd);
446 _watch_info_free (WatchInfo *info)
450 if (info->dir_table) {
451 g_hash_table_foreach (info->dir_table, _destroy_dir_table_entry, NULL);
452 g_hash_table_unref (info->dir_table);
454 if (info->wd_table) {
455 g_hash_table_foreach (info->wd_table, _destroy_wd_table_entry,
456 GINT_TO_POINTER(info->ifd));
457 g_hash_table_unref (info->wd_table);
459 if (info->ifd) close(info->ifd);
461 g_slice_free (WatchInfo, info);
471 _add_watch (int ifd, char *file_path, WatchInfo *info) {
472 GList *file_list = NULL;
474 gchar *file_name = NULL;
476 AddWatchResults res = WATCH_FAILED;
478 if (!file_path) return WATCH_FAILED;
480 file_name = g_path_get_basename (file_path);
481 if (!file_name) return WATCH_FAILED;
483 dir = g_path_get_dirname (file_path);
488 if ((file_list = (GList *)g_hash_table_lookup (
489 info->dir_table, (gconstpointer)dir))) {
490 file_list = g_list_append (file_list, file_name);
494 file_list = g_list_append (NULL, file_name);
495 g_hash_table_insert (info->dir_table, g_strdup(dir), file_list);
497 /* add watch on directory if its not existing */
498 if (g_access (dir, 0)) {
499 return _add_watch (ifd, dir, info);
502 DBG("Adding watch for file '%s' in dir '%s'", file_name, dir);
503 if ((wd = inotify_add_watch (ifd, dir, IN_CREATE)) == -1) {
504 WARN ("failed to add inotify watch on %s: %s", dir, strerror (errno));
506 goto remove_and_return;
509 if (!g_access (file_path, 0)) {
510 /* socket is ready, need not have a inotify watch for this */
511 inotify_rm_watch (ifd, wd);
513 goto remove_and_return;
516 g_hash_table_insert (info->wd_table, GINT_TO_POINTER(wd), dir);
521 g_hash_table_remove (info->dir_table, (gconstpointer)dir);
522 g_list_free_full (file_list, (GDestroyNotify)g_free);
528 _inotify_watcher_cb (gint ifd, GIOCondition condition, gpointer userdata)
530 WatchInfo *info = (WatchInfo *)userdata;
531 struct inotify_event *ie = NULL;
532 gsize size = sizeof (struct inotify_event) + PATH_MAX + 1;
533 guint nwatch = g_hash_table_size (info->wd_table);
535 ie = (struct inotify_event *) g_slice_alloc0(size);
537 read (ifd, ie, size) > (ssize_t)sizeof (struct inotify_event)) {
538 GList *file_list = NULL;
539 GList *element = NULL;
540 GList *pending_list = NULL;
541 gboolean is_first = FALSE;
542 gchar *file_path = NULL;
543 const gchar *dir = NULL;
545 dir = (gchar *)g_hash_table_lookup (
546 info->wd_table, GINT_TO_POINTER(ie->wd));
549 file_list = g_hash_table_lookup (info->dir_table, (gconstpointer)dir);
550 element = g_list_find_custom (file_list,
551 (gpointer)ie->name, (GCompareFunc)g_strcmp0);
553 DBG("Ignoring '%s' file creation", ie->name);
556 is_first = (file_list == element);
558 g_free (element->data);
559 file_list = g_list_delete_link(file_list, element);
561 g_hash_table_remove (info->dir_table, dir);
562 g_hash_table_remove (info->wd_table, GINT_TO_POINTER(ie->wd));
563 inotify_rm_watch (ifd, ie->wd);
565 } else if (is_first) {
566 g_hash_table_insert (info->dir_table, g_strdup (dir), file_list);
569 file_path = g_build_filename (dir, ie->name, NULL);
570 if ((pending_list = (GList *)g_hash_table_lookup (info->dir_table,
571 (gconstpointer)file_path)) != NULL) {
574 // as we are about add real inotify watch, first remove from dir_table
575 g_hash_table_steal (info->dir_table, (gconstpointer)file_path);
577 // Add watches to all the files depend on this directory
578 for (tmp = pending_list; tmp; tmp = tmp->next) {
579 gchar *file_name = (gchar *)tmp->data;
580 gchar *new_file_path = g_build_filename (file_path, file_name, NULL);
581 AddWatchResults res = _add_watch (ifd, new_file_path, info);
582 if (res == WATCH_READY) {
584 info->cb (new_file_path, nwatch == 0, NULL, info->userdata);
586 } else if (res == WATCH_ADDED) nwatch++;
588 WARN ("Couldn't add watch on '%s'", new_file_path);
591 g_free (new_file_path);
593 g_list_free (pending_list);
595 DBG("%s", file_path);
596 if (info->cb) info->cb (file_path, nwatch == 0, NULL, info->userdata);
601 g_slice_free1 (size, ie);
603 return nwatch ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
607 _expand_file_path (const gchar *file_path)
610 gchar **tmp_item =NULL;
611 gchar *expanded_path = NULL;
613 if (!file_path) return NULL;
616 * FIXME: we are not considering filename which having \$ in it
618 if (g_strrstr (file_path, "$") == NULL) return g_strdup(file_path);
620 items = g_strsplit (file_path, G_DIR_SEPARATOR_S, -1);
621 /* soemthing wrong in file path */
622 if (!items) { return g_strdup (file_path); }
624 for (tmp_item = items; *tmp_item; tmp_item++) {
625 char *item = *tmp_item;
626 if (item[0] == '$') {
627 const gchar *env = g_getenv (item+1);
629 *tmp_item = g_strdup (env ? env : "");
633 expanded_path = g_strjoinv (G_DIR_SEPARATOR_S, items);
637 return expanded_path;
641 tlm_utils_watch_for_files (
642 const gchar **watch_list,
648 WatchInfo *w_info = NULL;
650 if (!watch_list) return 0;
652 if ((ifd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC)) < 0) {
653 WARN("Failed to start inotify: %s", strerror(errno));
657 w_info = _watch_info_new (ifd, cb, userdata);
659 for (; *watch_list; watch_list++) {
660 char *socket_path = _expand_file_path (*watch_list);
661 AddWatchResults res = _add_watch (ifd, socket_path, w_info);
662 if (res == WATCH_FAILED) {
663 WARN ("Failed to watch for '%s'", socket_path);
664 } else if (res == WATCH_READY) {
665 gboolean is_final = !nwatch && !*(watch_list + 1);
666 if (cb) cb (socket_path, is_final, NULL, userdata);
673 _watch_info_free (w_info);
677 return g_unix_fd_add_full (G_PRIORITY_DEFAULT, ifd, G_IO_IN,
678 _inotify_watcher_cb, w_info, (GDestroyNotify)_watch_info_free);