1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <poll.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 
34 #include <sys/vt.h>
35 #include <linux/tty.h>
36 #include <linux/kd.h>
37 
38 #include <glib.h>
39 #include <glib/gstdio.h>
40 
41 #ifdef HAVE_PATHS_H
42 #include <paths.h>
43 #endif /* HAVE_PATHS_H */
44 
45 #ifdef HAVE_SYS_MOUNT_H
46 #include <sys/mount.h>
47 #endif
48 
49 #ifdef HAVE_SELINUX
50 #include <selinux/selinux.h>
51 #include <selinux/label.h>
52 #include <selinux/get_default_type.h>
53 #include <selinux/context.h>
54 #endif
55 
56 #include "ck-sysdeps.h"
57 
58 #ifndef ERROR
59 #define ERROR -1
60 #endif
61 
62 /* adapted from procps */
63 struct _CkProcessStat
64 {
65         int pid;
66         int ppid;                       /* stat,status     pid of parent process */
67         char state;                     /* stat,status     single-char code for process state (S=sleeping) */
68         char cmd[16];                   /* stat,status     basename of executable file in call to exec(2) */
69         unsigned long long utime;       /* stat            user-mode CPU time accumulated by process */
70         unsigned long long stime;       /* stat            kernel-mode CPU time accumulated by process */
71         unsigned long long cutime;      /* stat            cumulative utime of process and reaped children */
72         unsigned long long cstime;      /* stat            cumulative stime of process and reaped children */
73         unsigned long long start_time;  /* stat            start time of process -- seconds since 1-1-70 */
74         unsigned long start_code;       /* stat            address of beginning of code segment */
75         unsigned long end_code;         /* stat            address of end of code segment */
76         unsigned long start_stack;      /* stat            address of the bottom of stack for the process */
77         unsigned long kstk_esp;         /* stat            kernel stack pointer */
78         unsigned long kstk_eip;         /* stat            kernel instruction pointer */
79         unsigned long wchan;            /* stat (special)  address of kernel wait channel proc is sleeping in */
80         long priority;                  /* stat            kernel scheduling priority */
81         long nice;                      /* stat            standard unix nice level of process */
82         long rss;                       /* stat            resident set size from /proc/#/stat (pages) */
83         long alarm;                     /* stat            ? */
84         unsigned long rtprio;           /* stat            real-time priority */
85         unsigned long sched;            /* stat            scheduling class */
86         unsigned long vsize;            /* stat            number of pages of virtual memory ... */
87         unsigned long rss_rlim;         /* stat            resident set size limit? */
88         unsigned long flags;            /* stat            kernel flags for the process */
89         unsigned long min_flt;          /* stat            number of minor page faults since process start */
90         unsigned long maj_flt;          /* stat            number of major page faults since process start */
91         unsigned long cmin_flt;         /* stat            cumulative min_flt of process and child processes */
92         unsigned long cmaj_flt;         /* stat            cumulative maj_flt of process and child processes */
93         int     pgrp;                   /* stat            process group id */
94         int session;                    /* stat            session id */
95         int nlwp;                       /* stat    number of threads, or 0 if no clue */
96         int tty;                        /* stat            full device number of controlling terminal */
97         int tpgid;                      /* stat            terminal process group id */
98         int exit_signal;                /* stat            might not be SIGCHLD */
99         int processor;                  /* stat            current (or most recent?) CPU */
100 };
101 
102 /* adapted from procps */
103 #define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
104 #define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
105 
106 typedef struct tty_map_node {
107         struct tty_map_node *next;
108         guint major_number;
109         guint minor_first;
110         guint minor_last;
111         char name[32];
112         char devfs_type;
113 } tty_map_node;
114 
115 static tty_map_node *tty_map = NULL;
116 
117 #ifdef HAVE_SELINUX
118 static struct selabel_handle *label_hnd = NULL;
119 #endif
120 
121 /* adapted from procps */
122 /* Load /proc/tty/drivers for device name mapping use. */
123 static void
load_drivers(void)124 load_drivers (void)
125 {
126         char buf[10000];
127         char *p;
128         int fd;
129         int bytes;
130 
131         fd = open ("/proc/tty/drivers", O_RDONLY);
132         if (fd == -1) {
133                 goto fail;
134         }
135 
136         bytes = read (fd, buf, sizeof (buf) - 1);
137         if (bytes == -1) {
138                 goto fail;
139         }
140 
141         buf[bytes] = '\0';
142         p = buf;
143         while ((p = strstr (p, " " _PATH_DEV))) {
144                 tty_map_node *tmn;
145                 int len;
146                 char *end;
147 
148                 p += 6;
149                 end = strchr (p, ' ');
150                 if (! end) {
151                         continue;
152                 }
153                 len = end - p;
154                 tmn = calloc (1, sizeof (tty_map_node));
155                 tmn->next = tty_map;
156                 tty_map = tmn;
157                 /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
158                    keep a flag. */
159                 if (len >= 3 && !strncmp (end - 2, "%d", 2)) {
160                         len -= 2;
161                         tmn->devfs_type = 1;
162                 }
163                 strncpy (tmn->name, p, len);
164                 p = end; /* set p to point past the %d as well if there is one */
165                 while (*p == ' ') {
166                         p++;
167                 }
168 
169                 tmn->major_number = atoi (p);
170                 p += strspn (p, "0123456789");
171                 while (*p == ' ') {
172                         p++;
173                 }
174                 switch (sscanf (p, "%u-%u", &tmn->minor_first, &tmn->minor_last)) {
175                 default:
176                         /* Can't finish parsing this line so we remove it from the list */
177                         tty_map = tty_map->next;
178                         free (tmn);
179                         break;
180                 case 1:
181                         tmn->minor_last = tmn->minor_first;
182                         break;
183                 case 2:
184                         break;
185                 }
186         }
187  fail:
188         if (fd != -1) {
189                 close (fd);
190         }
191         if(! tty_map) {
192                 tty_map = (tty_map_node *)-1;
193         }
194 }
195 
196 /* adapted from procps */
197 /* Try to guess the device name from /proc/tty/drivers info. */
198 static char *
driver_name(guint maj,guint min)199 driver_name (guint maj,
200              guint min)
201 {
202         struct stat   sbuf;
203         tty_map_node *tmn;
204         char         *tty;
205 
206         if (! tty_map) {
207                 load_drivers ();
208         }
209         if (tty_map == (tty_map_node *) - 1) {
210                 return 0;
211         }
212 
213         tmn = tty_map;
214         for (;;) {
215                 if (! tmn) {
216                         return 0;
217                 }
218                 if (tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) {
219                         break;
220                 }
221                 tmn = tmn->next;
222         }
223 
224         tty = g_strdup_printf (_PATH_DEV "%s%d", tmn->name, min);  /* like "/dev/ttyZZ255" */
225         if (stat (tty, &sbuf) < 0){
226                 g_free (tty);
227 
228                 if (tmn->devfs_type) {
229                         return NULL;
230                 }
231 
232                 tty = g_strdup_printf (_PATH_DEV "%s", tmn->name);  /* like "/dev/ttyZZ255" */
233 
234                 if (stat (tty, &sbuf) < 0) {
235                         g_free (tty);
236                         return NULL;
237                 }
238         }
239 
240         if (min != MINOR_OF (sbuf.st_rdev)) {
241                 g_free (tty);
242                 return NULL;
243         }
244 
245         if (maj != MAJOR_OF (sbuf.st_rdev)) {
246                 g_free (tty);
247                 return NULL;
248         }
249 
250         return tty;
251 }
252 
253 /* adapted from procps */
254 static char *
link_name(guint maj,guint min,int pid,const char * name)255 link_name (guint       maj,
256            guint       min,
257            int         pid,
258            const char *name)
259 {
260         struct stat sbuf;
261         char       *path;
262         char       *tty;
263 
264         path = g_strdup_printf ("/proc/%d/%s", pid, name);
265         tty = g_file_read_link (path, NULL);
266         g_free (path);
267 
268         if (tty == NULL) {
269                 goto out;
270         }
271 
272         if (stat (tty, &sbuf) < 0) {
273                 g_free (tty);
274                 tty = NULL;
275                 goto out;
276         }
277 
278         if (min != MINOR_OF (sbuf.st_rdev)) {
279                 g_free (tty);
280                 tty = NULL;
281                 goto out;
282 
283         }
284         if (maj != MAJOR_OF (sbuf.st_rdev)) {
285                 g_free (tty);
286                 tty = NULL;
287                 goto out;
288         }
289 
290  out:
291         return tty;
292 }
293 
294 pid_t
ck_process_stat_get_ppid(CkProcessStat * stat)295 ck_process_stat_get_ppid (CkProcessStat *stat)
296 {
297         g_return_val_if_fail (stat != NULL, -1);
298 
299         return stat->ppid;
300 }
301 
302 char *
ck_process_stat_get_cmd(CkProcessStat * stat)303 ck_process_stat_get_cmd (CkProcessStat *stat)
304 {
305         g_return_val_if_fail (stat != NULL, NULL);
306 
307         return g_strdup (stat->cmd);
308 }
309 
310 /* adapted from procps */
311 char *
ck_process_stat_get_tty(CkProcessStat * stat)312 ck_process_stat_get_tty (CkProcessStat *stat)
313 {
314         guint dev;
315         char *tty;
316         guint dev_maj;
317         guint dev_min;
318         pid_t pid;
319 
320         g_return_val_if_fail (stat != NULL, NULL);
321 
322         pid = stat->pid;
323         dev = stat->tty;
324 
325         if (dev == 0u) {
326                 return NULL;
327         }
328 
329         dev_maj = MAJOR_OF (dev);
330         dev_min = MINOR_OF (dev);
331 
332         tty = link_name (dev_maj, dev_min, pid, "tty");
333         if (tty != NULL) {
334                 goto out;
335         }
336 
337         tty = driver_name (dev_maj, dev_min);
338         if (tty != NULL) {
339                 goto out;
340         }
341 
342         tty = link_name (dev_maj, dev_min, pid, "fd/2");
343         if (tty != NULL) {
344                 goto out;
345         }
346 
347         tty = link_name (dev_maj, dev_min, pid, "fd/255");
348         if (tty != NULL) {
349                 goto out;
350         }
351 
352  out:
353 
354         return tty;
355 }
356 
357 #define KLF "l"
358 /* adapted from procps */
359 static void
stat2proc(const char * S,CkProcessStat * P)360 stat2proc (const char    *S,
361            CkProcessStat *P)
362 {
363         unsigned num;
364         char   * tmp;
365 
366         /* fill in default values for older kernels */
367         P->processor = 0;
368         P->rtprio = -1;
369         P->sched = -1;
370         P->nlwp = 0;
371 
372         S = strchr (S, '(') + 1;
373         tmp = strrchr (S, ')');
374         num = tmp - S;
375         if (G_UNLIKELY (num >= sizeof P->cmd)) {
376                 num = sizeof P->cmd - 1;
377         }
378 
379         memcpy (P->cmd, S, num);
380         P->cmd[num] = '\0';
381         S = tmp + 2;                 /* skip ") " */
382 
383         num = sscanf (S,
384                       "%c " /* state */
385                       "%d %d %d %d %d " /* ppid pgrp session tty tpgid */
386                       "%lu %lu %lu %lu %lu " /* flags min_flt cmin_flt maj_flt cmaj_flt */
387                       "%Lu %Lu %Lu %Lu "  /* utime stime cutime cstime */
388                       "%ld %ld " /* priority nice*/
389                       "%d " /* nlwp */
390                       "%ld " /* alarm */
391                       "%Lu "  /* start_time */
392                       "%lu " /* vsize */
393                       "%ld " /* rss */
394                       "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u " /* rss_rlim start_code end_code start_stack kstk_esp kstk_eip */
395                       "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
396                       "%"KLF"u %*u %*u " /* wchan */
397                       "%d %d " /* exit_signal processor */
398                       "%lu %lu", /* rtprio sched */
399                       &P->state,
400                       &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
401                       &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
402                       &P->utime, &P->stime, &P->cutime, &P->cstime,
403                       &P->priority, &P->nice,
404                       &P->nlwp,
405                       &P->alarm,
406                       &P->start_time,
407                       &P->vsize,
408                       &P->rss,
409                       &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
410                       /*     P->signal, P->blocked, P->sigignore, P->sigcatch,   */ /* can't use */
411                       &P->wchan, /* &P->nswap, &P->cnswap, */  /* nswap and cnswap dead for 2.4.xx and up */
412                       /* -- Linux 2.0.35 ends here -- */
413                       &P->exit_signal, &P->processor,  /* 2.2.1 ends with "exit_signal" */
414                       /* -- Linux 2.2.8 to 2.5.17 end here -- */
415                       &P->rtprio, &P->sched  /* both added to 2.5.18 */
416                       );
417 
418         if (!P->nlwp){
419                 P->nlwp = 1;
420         }
421 }
422 
423 gboolean
ck_process_stat_new_for_unix_pid(pid_t pid,CkProcessStat ** stat,GError ** error)424 ck_process_stat_new_for_unix_pid (pid_t           pid,
425                                   CkProcessStat **stat,
426                                   GError        **error)
427 {
428         char          *path;
429         char          *contents;
430         gsize          length;
431         gboolean       res;
432         GError        *local_error;
433         CkProcessStat *proc;
434 
435         g_return_val_if_fail (pid > 1, FALSE);
436 
437         if (stat == NULL) {
438                 return FALSE;
439         }
440 
441         path = g_strdup_printf ("/proc/%d/stat", pid);
442 
443         contents = NULL;
444         local_error = NULL;
445         res = g_file_get_contents (path,
446                                    &contents,
447                                    &length,
448                                    &local_error);
449         if (res) {
450                 proc = g_new0 (CkProcessStat, 1);
451                 proc->pid = pid;
452                 stat2proc (contents, proc);
453                 *stat = proc;
454         } else {
455                 g_propagate_error (error, local_error);
456                 *stat = NULL;
457         }
458 
459         g_free (contents);
460         g_free (path);
461 
462         return res;
463 }
464 
465 void
ck_process_stat_free(CkProcessStat * stat)466 ck_process_stat_free (CkProcessStat *stat)
467 {
468         g_free (stat);
469 }
470 
471 GHashTable *
ck_unix_pid_get_env_hash(pid_t pid)472 ck_unix_pid_get_env_hash (pid_t pid)
473 {
474         char       *path;
475         gboolean    res;
476         char       *contents;
477         gsize       length;
478         GError     *error;
479         GHashTable *hash;
480         gsize       i;
481         gboolean    last_was_null;
482 
483         g_return_val_if_fail (pid > 1, NULL);
484 
485         contents = NULL;
486         hash = NULL;
487 
488         path = g_strdup_printf ("/proc/%u/environ", (guint)pid);
489 
490         error = NULL;
491         res = g_file_get_contents (path,
492                                    &contents,
493                                    &length,
494                                    &error);
495         if (! res) {
496                 /* This is pretty harmless, usually it means the process
497                  * was short lived and we didn't get around to looking at
498                  * it before it died. */
499                 g_debug ("Couldn't read %s: %s", path, error->message);
500                 g_error_free (error);
501                 goto out;
502         }
503 
504         hash = g_hash_table_new_full (g_str_hash,
505                                       g_str_equal,
506                                       g_free,
507                                       g_free);
508 
509         last_was_null = TRUE;
510         for (i = 0; i < length; i++) {
511                 if (contents[i] == '\0') {
512                         last_was_null = TRUE;
513                         continue;
514                 }
515                 if (last_was_null) {
516                         char **vals;
517                         vals = g_strsplit (contents + i, "=", 2);
518                         if (vals != NULL) {
519                                 g_hash_table_insert (hash,
520                                                      g_strdup (vals[0]),
521                                                      g_strdup (vals[1]));
522                                 g_strfreev (vals);
523                         }
524                 }
525                 last_was_null = FALSE;
526         }
527 
528  out:
529         g_free (contents);
530         g_free (path);
531 
532         return hash;
533 }
534 
535 char *
ck_unix_pid_get_env(pid_t pid,const char * var)536 ck_unix_pid_get_env (pid_t       pid,
537                      const char *var)
538 {
539         char      *path;
540         gboolean   res;
541         char      *contents;
542         char      *val;
543         gsize      length;
544         GError    *error;
545         guint      i;
546         char      *prefix;
547         int        prefix_len;
548         gboolean   last_was_null;
549 
550         g_return_val_if_fail (pid > 1, NULL);
551 
552         val = NULL;
553         contents = NULL;
554         prefix = NULL;
555 
556         path = g_strdup_printf ("/proc/%u/environ", (guint)pid);
557 
558         error = NULL;
559         res = g_file_get_contents (path,
560                                    &contents,
561                                    &length,
562                                    &error);
563         if (! res) {
564                 g_warning ("Couldn't read %s: %s", path, error->message);
565                 g_error_free (error);
566                 goto out;
567         }
568 
569 
570         prefix = g_strdup_printf ("%s=", var);
571         prefix_len = strlen (prefix);
572 
573         /* FIXME: make more robust */
574         last_was_null = TRUE;
575         for (i = 0; i < length; i++) {
576                 if (contents[i] == '\0') {
577                         last_was_null = TRUE;
578                         continue;
579                 }
580                 if (last_was_null && g_str_has_prefix (contents + i, prefix)) {
581                         val = g_strdup (contents + i + prefix_len);
582                         break;
583                 }
584                 last_was_null = FALSE;
585         }
586 
587  out:
588         g_free (prefix);
589         g_free (contents);
590         g_free (path);
591 
592         return val;
593 }
594 
595 uid_t
ck_unix_pid_get_uid(pid_t pid)596 ck_unix_pid_get_uid (pid_t pid)
597 {
598         struct stat st;
599         char       *path;
600         int         uid;
601         int         res;
602 
603         g_return_val_if_fail (pid > 1, 0);
604 
605         uid = -1;
606 
607         path = g_strdup_printf ("/proc/%u", (guint)pid);
608         res = stat (path, &st);
609         g_free (path);
610 
611         if (res == 0) {
612                 uid = st.st_uid;
613         }
614 
615         return uid;
616 }
617 
618 pid_t
ck_unix_pid_get_ppid(pid_t pid)619 ck_unix_pid_get_ppid (pid_t pid)
620 {
621         int            ppid;
622         gboolean       res;
623         CkProcessStat *stat;
624 
625         g_return_val_if_fail (pid > 1, 0);
626 
627         ppid = -1;
628 
629         res = ck_process_stat_new_for_unix_pid (pid, &stat, NULL);
630         if (! res) {
631                 goto out;
632         }
633 
634         ppid = ck_process_stat_get_ppid (stat);
635 
636         ck_process_stat_free (stat);
637 
638  out:
639         return ppid;
640 }
641 
642 gboolean
ck_unix_pid_get_login_session_id(pid_t pid,char ** idp)643 ck_unix_pid_get_login_session_id (pid_t  pid,
644                                   char **idp)
645 {
646         gboolean ret;
647         gboolean res;
648         char    *path;
649         char    *contents;
650         gsize    length;
651         GError  *error;
652         char    *end_of_valid_ulong;
653         gulong   ulong_value;
654 
655         g_return_val_if_fail (pid > 1, FALSE);
656 
657         ret = FALSE;
658         contents = NULL;
659 
660         path = g_strdup_printf ("/proc/%u/sessionid", (guint)pid);
661 
662         error = NULL;
663         res = g_file_get_contents (path,
664                                    &contents,
665                                    &length,
666                                    &error);
667         if (! res) {
668                 g_warning ("Couldn't read %s: %s", path, error->message);
669                 g_error_free (error);
670                 goto out;
671         }
672 
673         if (contents[0] == '\0') {
674                 g_warning ("Couldn't read %s: empty file", path);
675                 goto out;
676         }
677 
678         errno = 0;
679         ulong_value = strtoul (contents, &end_of_valid_ulong, 10);
680 
681         if (*end_of_valid_ulong != '\0') {
682                 goto out;
683         }
684 
685         if (errno == ERANGE) {
686                 g_warning ("Couldn't read %s: %s", path, g_strerror (errno));
687                 goto out;
688         }
689 
690         /* Will be G_MAXUINT32 if it isn't set */
691         if (ulong_value == G_MAXUINT32) {
692                 goto out;
693         }
694 
695         if (idp != NULL) {
696                 *idp = g_strdup_printf ("%lu", (unsigned long int)ulong_value);
697         }
698 
699         ret = TRUE;
700 
701  out:
702         g_free (contents);
703         g_free (path);
704 
705         return ret;
706 }
707 
708 gboolean
ck_get_max_num_consoles(guint * num)709 ck_get_max_num_consoles (guint *num)
710 {
711         if (num != NULL) {
712                 *num = MAX_NR_CONSOLES;
713         }
714 
715         return TRUE;
716 }
717 
718 gboolean
ck_supports_activatable_consoles(void)719 ck_supports_activatable_consoles (void)
720 {
721         return TRUE;
722 }
723 
724 char *
ck_get_console_device_for_num(guint num)725 ck_get_console_device_for_num (guint num)
726 {
727         char *device;
728 
729         device = g_strdup_printf (_PATH_TTY "%u", num);
730 
731         return device;
732 }
733 
734 gboolean
ck_get_console_num_from_device(const char * device,guint * num)735 ck_get_console_num_from_device (const char *device,
736                                 guint      *num)
737 {
738         guint    n;
739         gboolean ret;
740 
741         n = 0;
742         ret = FALSE;
743 
744         if (device == NULL) {
745                 return FALSE;
746         }
747 
748         if (sscanf (device, _PATH_TTY "%u", &n) == 1) {
749                 ret = TRUE;
750         }
751 
752         if (num != NULL) {
753                 *num = n;
754         }
755 
756         return ret;
757 }
758 
759 gboolean
ck_get_active_console_num(int console_fd,guint * num)760 ck_get_active_console_num (int    console_fd,
761                            guint *num)
762 {
763         gboolean       ret;
764         int            res;
765         guint          active;
766         struct vt_stat stat;
767 
768         g_assert (console_fd != -1);
769 
770         active = 0;
771         ret = FALSE;
772 
773         res = ioctl (console_fd, VT_GETSTATE, &stat);
774         if (res == ERROR) {
775                 perror ("ioctl VT_GETSTATE");
776                 goto out;
777         }
778 
779         {
780                 int i;
781 
782                 g_debug ("Current VT: tty%d", stat.v_active);
783                 for (i = 1; i <= 16; i++) {
784                         gboolean is_on;
785                         is_on = stat.v_state & (1 << i);
786 
787                         g_debug ("VT %d:%s", i, is_on ? "on" : "off");
788                 }
789         }
790 
791         active = stat.v_active;
792         ret = TRUE;
793 
794  out:
795         if (num != NULL) {
796                 *num = active;
797         }
798 
799         return ret;
800 }
801 
802 /* adapted from upower-0.9 branch */
803 static gfloat
linux_get_used_swap(void)804 linux_get_used_swap (void)
805 {
806         gchar *contents = NULL;
807         gchar **lines = NULL;
808         GError *error = NULL;
809         gchar **tokens;
810         gboolean ret;
811         guint active = 0;
812         guint swap_free = 0;
813         guint swap_total = 0;
814         guint len;
815         guint i;
816         gfloat percentage = 0.0f;
817         const gchar *filename = "/proc/meminfo";
818 
819         /* get memory data */
820         ret = g_file_get_contents (filename, &contents, NULL, &error);
821         if (!ret) {
822                 g_warning ("failed to open %s: %s", filename, error->message);
823                 g_error_free (error);
824                 goto out;
825         }
826 
827         /* process each line */
828         lines = g_strsplit (contents, "\n", -1);
829         for (i=1; lines[i] != NULL; i++) {
830                 tokens = g_strsplit_set (lines[i], ": ", -1);
831                 len = g_strv_length (tokens);
832                 if (len > 3) {
833                         if (g_strcmp0 (tokens[0], "SwapFree") == 0)
834                                 swap_free = atoi (tokens[len-2]);
835                         if (g_strcmp0 (tokens[0], "SwapTotal") == 0)
836                                 swap_total = atoi (tokens[len-2]);
837                         else if (g_strcmp0 (tokens[0], "Active(anon)") == 0)
838                                 active = atoi (tokens[len-2]);
839                 }
840                 g_strfreev (tokens);
841         }
842 
843         /* first check if we even have swap, if not consider all swap space used */
844         if (swap_total == 0) {
845                 g_debug ("no swap space found");
846                 percentage = 100.0f;
847                 goto out;
848         }
849         /* work out how close to the line we are */
850         if (swap_free > 0 && active > 0)
851                 percentage = (active * 100) / swap_free;
852         g_debug ("total swap available %i kb, active memory %i kb (%.1f%%)", swap_free, active, percentage);
853 out:
854         g_free (contents);
855         g_strfreev (lines);
856         return percentage;
857 }
858 
859 /* adapted from upower-0.9 branch */
860 static gboolean
linux_check_enough_swap(void)861 linux_check_enough_swap (void)
862 {
863         gfloat waterline = 98.0f; /* 98% */
864         gfloat used_swap = linux_get_used_swap();
865 
866         if (used_swap < waterline) {
867                         g_debug ("enough swap to hibernate");
868                         return TRUE;
869                 } else {
870                         g_debug ("not enough swap to hibernate");
871                         return FALSE;
872                 }
873 
874         g_debug ("should not hit this in linux_check_enough_swap");
875         return FALSE;
876 }
877 
878 
879 static gboolean
linux_supports_sleep_state(const gchar * state)880 linux_supports_sleep_state (const gchar *state)
881 {
882         gboolean ret = FALSE;
883         gchar *command;
884         GError *error = NULL;
885         gint exit_status;
886 
887         /* run script from pm-utils */
888         command = g_strdup_printf ("/usr/bin/pm-is-supported --%s", state);
889         g_debug ("excuting command: %s", command);
890         ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error);
891         if (!ret) {
892                 g_warning ("failed to run script: %s", error->message);
893                 g_error_free (error);
894                 goto out;
895         }
896         ret = (WIFEXITED(exit_status) && (WEXITSTATUS(exit_status) == EXIT_SUCCESS));
897 
898 out:
899         g_free (command);
900 
901         return ret;
902 }
903 
904 gboolean
ck_system_can_suspend(void)905 ck_system_can_suspend (void)
906 {
907         return linux_supports_sleep_state ("suspend");
908 }
909 
910 gboolean
ck_system_can_hibernate(void)911 ck_system_can_hibernate (void)
912 {
913         if (linux_supports_sleep_state ("hibernate"))
914 		return linux_check_enough_swap() ;
915         return FALSE;
916 }
917 
918 gboolean
ck_system_can_hybrid_sleep(void)919 ck_system_can_hybrid_sleep (void)
920 {
921         return linux_supports_sleep_state ("suspend-hybrid");
922 }
923 
924 static gboolean
ck_selinux_open(void)925 ck_selinux_open(void)
926 {
927 #ifdef HAVE_SELINUX
928         TRACE ();
929 
930         if (label_hnd)
931                 return TRUE;
932 
933         if (is_selinux_enabled() <= 0)
934                 return TRUE;
935 
936         label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
937         if (label_hnd) {
938                 return TRUE;
939         } else {
940                 g_info ("Failed to open selabel handle, reason was: %s", strerror(errno));
941                 errno = 0;
942 
943                 // do not fail in permissive mode
944                 return (security_getenforce() == 1) ? FALSE : TRUE;
945         }
946 #endif
947 
948         return TRUE;
949 }
950 
951 static void
ck_selinux_close(void)952 ck_selinux_close(void)
953 {
954 #ifdef HAVE_SELINUX
955         if (label_hnd) {
956                 selabel_close(label_hnd);
957                 label_hnd = NULL;
958         }
959 #endif
960 }
961 
962 static gchar*
ck_selinux_lookup_context(const gchar * dest)963 ck_selinux_lookup_context(const gchar *dest)
964 {
965 #ifdef HAVE_SELINUX
966         int rc;
967         GStatBuf st;
968         mode_t mode = 0;
969         security_context_t con;
970         gchar *constr;
971 
972         if (!label_hnd)
973                 return NULL;
974 
975         errno = 0;
976         memset(&st, 0, sizeof(st));
977         rc = g_lstat(dest, &st);
978         if (rc == 0)
979                 mode = st.st_mode;
980         else if (errno != ENOENT)
981                 return NULL;
982 
983         errno = 0;
984         rc = selabel_lookup_raw(label_hnd, &con, dest, mode);
985         if (rc < 0 && errno != ENOENT) {
986                 errno = 0;
987                 return NULL;
988         }
989 
990         constr = g_strdup(con);
991         freecon(con);
992 
993         return constr;
994 #endif
995 
996         return NULL;
997 }
998 
999 gboolean
ck_make_tmpfs(guint uid,guint gid,const gchar * dest)1000 ck_make_tmpfs (guint uid, guint gid, const gchar *dest)
1001 {
1002 #ifdef HAVE_SYS_MOUNT_H
1003         gchar        *opts;
1004         gchar        *context;
1005         int           result;
1006 
1007         TRACE ();
1008 
1009         context = ck_selinux_lookup_context(dest);
1010         if (context) {
1011                 opts = g_strdup_printf ("mode=0700,uid=%d,rootcontext=%s", uid, context);
1012         } else {
1013                 opts = g_strdup_printf ("mode=0700,uid=%d", uid);
1014         }
1015 
1016         g_debug ("mounting tmpfs. uid=%d, gid=%d, dest=%s, opts=%s", uid, gid, dest, opts);
1017         result = mount("none", dest, "tmpfs", 0, opts);
1018 
1019         g_free (context);
1020         g_free (opts);
1021 
1022         if (result == 0) {
1023                 return TRUE;
1024         } else {
1025                 g_info ("Failed to create tmpfs mount, reason was: %s", strerror(errno));
1026                 errno = 0;
1027                 return FALSE;
1028         }
1029 #endif
1030 
1031         return FALSE;
1032 }
1033 
1034 gboolean
ck_remove_tmpfs(guint uid,const char * dest)1035 ck_remove_tmpfs (guint uid, const char* dest)
1036 {
1037 #ifdef HAVE_SYS_MOUNT_H
1038         int           result;
1039 
1040         TRACE ();
1041 
1042         result = umount2(dest, MNT_DETACH);
1043 
1044         if (result == 0) {
1045                 return TRUE;
1046         }
1047 
1048         g_info ("Failed to unmount tmpfs mount, reason was: %s", strerror(errno));
1049         errno = 0;
1050 #endif
1051 
1052         return FALSE;
1053 }
1054 
1055 #ifdef HAVE_SYS_VT_SIGNAL
1056 /* For the moment this is Linux only.
1057  * Returns the vt file descriptor or < 0 on failure.
1058  * /sys/class/tty/tty0/active on Linux
1059  */
1060 gint
ck_get_vt_signal_fd(void)1061 ck_get_vt_signal_fd (void)
1062 {
1063         gint fd;
1064         const char *errmsg = NULL;
1065 
1066         errno = 0;
1067         /* Open the magic Linux location */
1068         fd = open ("/sys/class/tty/tty0/active", O_RDONLY);
1069         if (fd < 0) {
1070                 errmsg = g_strerror (errno);
1071                 g_error ("ck_get_vt_signal_fd: Error opening sys file: %s",
1072                          errmsg);
1073         }
1074 
1075         return fd;
1076 }
1077 
1078 static gboolean
poll_vt_fd(gint sys_fd)1079 poll_vt_fd (gint sys_fd)
1080 {
1081         struct pollfd fds[1];
1082         const char *errmsg = NULL;
1083 
1084         errno = 0;
1085         fds[0].fd = sys_fd;
1086         fds[0].events = POLLPRI;
1087 
1088         for(;;) {
1089                 g_debug ("poll_vt_fd: polling");
1090                 if (poll(fds, 1, -1) < 0) {
1091                         errmsg = g_strerror (errno);
1092 
1093                         /* Error handling */
1094                         if (errno == EINTR || errno == EAGAIN) {
1095                                 g_debug ("poll_vt_fd: Interrupted while waiting for vt event: %s",
1096                                           errmsg);
1097                                 /* try again */
1098                         } else {
1099                                 g_error ("poll_vt_fd: Error polling for vt event: %s",
1100                                          errmsg);
1101                                 return FALSE;
1102                         }
1103                 } else if (fds[0].revents & POLLPRI) {
1104                         /* There's something we care about! */
1105                         return TRUE;
1106                 } else {
1107                         /* Something we don't care about */
1108                 }
1109         }
1110 
1111         return FALSE;
1112 }
1113 
1114 static gint
read_from_vt_fd(gint sys_fd)1115 read_from_vt_fd (gint sys_fd)
1116 {
1117         gint ret = -1;
1118         char new_vt[32];
1119         ssize_t bytes_read = 0;
1120 
1121         new_vt[31] = '\0';
1122 
1123         while (bytes_read == 0) {
1124                 errno = 0;
1125 
1126                 bytes_read = read (sys_fd, &new_vt, 30);
1127                 if (bytes_read == -1 && (errno == EAGAIN || errno == EINTR)) {
1128                         g_debug ("read_from_vt_fd: Interrupted while reading from sys_fd: %s",
1129                                  g_strerror (errno));
1130 
1131                         bytes_read = 0;
1132                         /* try again */
1133                 } else if (bytes_read == -1) {
1134                         g_error ("read_from_vt_fd: Error while reading from sys_fd: %s",
1135                                  g_strerror (errno));
1136                         ret = -1;
1137                 } else {
1138                         gchar unused[32];
1139                         gint  vty_num;
1140 
1141                         g_debug ("read_from_vt_fd: got %s", new_vt);
1142 
1143                         if(sscanf (new_vt, "%31[a-z-A-Z]%d", unused, &vty_num) == 2) {
1144                                 g_debug ("read_from_vt_fd: parsed as %s %d", unused, vty_num);
1145                                 ret = vty_num;
1146                         }
1147 
1148                         /* back to the beginning of the fd */
1149                         if (lseek(sys_fd, 0,SEEK_SET) < 0)
1150                         {
1151                                 g_debug ("read_from_vt_fd: error seeking to beginning of file %s",
1152                                          g_strerror (errno));
1153                                 ret = -1;
1154                         }
1155                 }
1156         }
1157 
1158         return ret;
1159 }
1160 
1161 /*
1162  * Returns FALSE if something went wrong with reading/polling the
1163  * vt fd for VT changes.
1164  */
1165 gboolean
ck_wait_for_console_switch(gint sys_fd,gint32 * num)1166 ck_wait_for_console_switch (gint sys_fd, gint32 *num)
1167 {
1168         gint new_vt = -1;
1169 
1170         g_debug ("ck_wait_for_console_switch: sys_fd opened");
1171 
1172         /* Poll for changes */
1173         if (poll_vt_fd (sys_fd)) {
1174                 g_debug ("ck_wait_for_console_switch: poll_vt_fd returned");
1175                 /* Read the changes */
1176                 new_vt = read_from_vt_fd (sys_fd);
1177                 if (new_vt >= 0) {
1178                         g_debug ("ck_wait_for_console_switch: read successful");
1179                         /* success, update */
1180                         if (num != NULL) {
1181                                 *num = new_vt;
1182                         }
1183                 }
1184         }
1185 
1186         return new_vt < 0 ? FALSE : TRUE;
1187 }
1188 #endif /* HAVE_SYS_VT_SIGNAL */
1189 
1190 gboolean
ck_sysdeps_init(void)1191 ck_sysdeps_init (void)
1192 {
1193         return ck_selinux_open();
1194 }
1195 
1196 void
ck_sysdeps_fini(void)1197 ck_sysdeps_fini (void)
1198 {
1199         ck_selinux_close();
1200 }
1201