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