You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
8.1 KiB

  1. /* plymouth.c - Plymouth plugin for the OpenRC init system
  2. *
  3. * Copyright (C) 2011 Amadeusz Żołnowski <aidecoe@gentoo.org>
  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. #include <assert.h>
  20. #include <einfo.h>
  21. #include <rc.h>
  22. #include <stdarg.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <fcntl.h>
  26. #include <ctype.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. #include <sys/stat.h>
  30. #ifdef DEBUG
  31. # define DBG(x) einfo("[plymouth-plugin] " x)
  32. #else
  33. # define DBG(x)
  34. #endif
  35. #define BUFFER_SIZE 300
  36. #define RWDIR (R_OK | W_OK | X_OK)
  37. #ifndef RUN_DIR
  38. #define RUN_DIR "/run/plymouth"
  39. #endif
  40. #ifndef PID_FILE
  41. #define PID_FILE RUN_DIR "/pid"
  42. #endif
  43. enum {
  44. PLY_MODE_BOOT,
  45. PLY_MODE_SHUTDOWN
  46. };
  47. int command(const char* cmd)
  48. {
  49. int rv = system(cmd);
  50. #if DEBUG
  51. if(rv != 0) {
  52. ewarn("[plymouth-plugin] command(\"%s\"): rv=%d", cmd, rv);
  53. }
  54. #endif
  55. return rv;
  56. }
  57. int commandf(const char* cmd, ...)
  58. {
  59. static char buffer[BUFFER_SIZE];
  60. va_list ap;
  61. int rv;
  62. va_start(ap, cmd);
  63. rv = vsnprintf(buffer, BUFFER_SIZE, cmd, ap);
  64. va_end(ap);
  65. if(rv >= BUFFER_SIZE) {
  66. eerror("[plymouth-plugin] command(\"%s\"): buffer overflow", buffer);
  67. return -1;
  68. }
  69. return command(buffer);
  70. }
  71. void restart_console_font()
  72. {
  73. if(RC_SERVICE_STARTED == rc_service_state("consolefont"))
  74. command("/etc/init.d/consolefont restart");
  75. }
  76. bool ply_message(const char* hook, const char* name)
  77. {
  78. return true;
  79. }
  80. bool ply_ping()
  81. {
  82. return (system("/bin/plymouth --ping") == 0);
  83. }
  84. bool ply_quit(int mode)
  85. {
  86. int rv = 0;
  87. if(mode == PLY_MODE_BOOT)
  88. rv = command("/bin/plymouth quit");
  89. else if(mode == PLY_MODE_SHUTDOWN)
  90. rv = command("/bin/plymouth quit --retain-splash");
  91. else
  92. assert(0 && "Unknown mode");
  93. return (rv == 0);
  94. }
  95. bool ply_start(int mode)
  96. {
  97. int rv = 0;
  98. if(!ply_ping()) {
  99. ebegin("Starting plymouthd");
  100. if(access(RUN_DIR, RWDIR) != 0) {
  101. if(mkdir(RUN_DIR, 0755) != 0) {
  102. eerror("[plymouth-plugin] Couldn't create " RUN_DIR);
  103. return false;
  104. }
  105. }
  106. #define PLYD "/sbin/plymouthd --attach-to-session --pid-file=" PID_FILE \
  107. " --tty=/dev/tty1 --mode="
  108. if(mode == PLY_MODE_BOOT)
  109. rv = command(PLYD "boot");
  110. else if(mode == PLY_MODE_SHUTDOWN)
  111. rv = command(PLYD "shutdown");
  112. else
  113. assert(0 && "Unknown mode");
  114. #undef PLYD
  115. eend(rv, "");
  116. if((rv == 0) && command("/bin/plymouth --show-splash") != 0)
  117. return false;
  118. }
  119. return true;
  120. }
  121. bool ply_update_status(int hook, const char* name)
  122. {
  123. return (commandf("/bin/plymouth update --status=%d-%s", hook, name) == 0);
  124. }
  125. bool kernel_command_has_argument(const char *argument)
  126. {
  127. char buf[4096];
  128. int fd;
  129. fd = open("/proc/cmdline", O_RDONLY);
  130. if( fd < 0 ) {
  131. return false;
  132. }
  133. if(read(fd, buf, 4095) < 0) {
  134. close(fd);
  135. return false;
  136. }
  137. close(fd);
  138. char *found_arg = strstr(buf, argument);
  139. if(found_arg == NULL)
  140. return false;
  141. if(found_arg == buf || found_arg[-1] == ' ') {
  142. char endch = *(found_arg+strlen(argument));
  143. return (isspace(endch) || endch == '\0') ? true : false;
  144. }
  145. return false;
  146. }
  147. bool ply_update_rootfs_rw()
  148. {
  149. const int rwdir = RWDIR;
  150. if(access("/var/lib/plymouth", rwdir) != 0
  151. || access("/var/log", rwdir) != 0) {
  152. eerror("[plymouth-plugin] /var/lib/plymouth and /var/log need to be "
  153. "writable at this stage, but are not!");
  154. return false;
  155. }
  156. return (command("/bin/plymouth update-root-fs --read-write") == 0);
  157. }
  158. int rc_plugin_hook(RC_HOOK hook, const char *name)
  159. {
  160. int rv = 0;
  161. char* runlevel = rc_runlevel_get();
  162. const char* bootlevel = getenv("RC_BOOTLEVEL");
  163. const char* defaultlevel = getenv("RC_DEFAULTLEVEL");
  164. #ifdef DEBUG
  165. einfo("hook=%d name=%s runlvl=%s plyd=%d", hook, name, runlevel,
  166. ply_ping());
  167. #endif
  168. /* Don't do anything if we're not booting or shutting down. */
  169. if(!(rc_runlevel_starting() || rc_runlevel_stopping())) {
  170. switch(hook) {
  171. case RC_HOOK_RUNLEVEL_STOP_IN:
  172. case RC_HOOK_RUNLEVEL_STOP_OUT:
  173. case RC_HOOK_RUNLEVEL_START_IN:
  174. case RC_HOOK_RUNLEVEL_START_OUT:
  175. /* Switching runlevels, so we're booting or shutting down.*/
  176. break;
  177. default:
  178. DBG("Not booting or shutting down");
  179. goto exit;
  180. }
  181. }
  182. DBG("switch1");
  183. switch(hook) {
  184. case RC_HOOK_RUNLEVEL_START_IN:
  185. case RC_HOOK_RUNLEVEL_STOP_IN:
  186. case RC_HOOK_SERVICE_START_IN:
  187. case RC_HOOK_SERVICE_STOP_IN:
  188. ply_update_status(hook, name);
  189. break;
  190. default:
  191. break;
  192. }
  193. DBG("switch2");
  194. switch(hook) {
  195. case RC_HOOK_RUNLEVEL_STOP_IN:
  196. /* Start the Plymouth daemon and show splash when system is being shut
  197. * down. */
  198. if(strcmp(name, RC_LEVEL_SHUTDOWN) == 0 &&
  199. (kernel_command_has_argument("splash") ||
  200. kernel_command_has_argument("splash=shutdown")))
  201. {
  202. DBG("ply_start(PLY_MODE_SHUTDOWN)");
  203. if(!ply_start(PLY_MODE_SHUTDOWN)
  204. || !ply_update_rootfs_rw())
  205. rv = 1;
  206. command("/usr/bin/chvt 1");
  207. }
  208. break;
  209. case RC_HOOK_RUNLEVEL_START_IN:
  210. /* Start the Plymouth daemon and show splash when entering the boot
  211. * runlevel. Required /proc and /sys should already be mounted in
  212. * sysinit runlevel. */
  213. if(strcmp(name, bootlevel) == 0 &&
  214. (kernel_command_has_argument("splash") ||
  215. kernel_command_has_argument("splash=silent")))
  216. {
  217. DBG("ply_start(PLY_MODE_BOOT)");
  218. if(!ply_start(PLY_MODE_BOOT))
  219. rv = 1;
  220. }
  221. break;
  222. case RC_HOOK_RUNLEVEL_START_OUT:
  223. /* Stop the Plymouth daemon right after default runlevel is started. */
  224. if(strcmp(name, defaultlevel) == 0) {
  225. DBG("ply_quit(PLY_MODE_BOOT)");
  226. if(!ply_quit(PLY_MODE_BOOT))
  227. rv = 1;
  228. restart_console_font();
  229. }
  230. break;
  231. case RC_HOOK_SERVICE_STOP_IN:
  232. /* Quit Plymouth when we're going to lost write access to /var/... */
  233. if(strcmp(name, "mount-ro") == 0 &&
  234. strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0) {
  235. DBG("ply_quit(PLY_MODE_SHUTDOWN)");
  236. if(!ply_quit(PLY_MODE_SHUTDOWN))
  237. rv = 1;
  238. }
  239. break;
  240. case RC_HOOK_SERVICE_STOP_NOW:
  241. if(!ply_message("Stopping service", name))
  242. rv = 1;
  243. if(strcmp(name, "xdm") == 0)
  244. command("/usr/bin/chvt 1");
  245. break;
  246. case RC_HOOK_SERVICE_STOP_DONE:
  247. if(strcmp(name, "xdm") == 0)
  248. command("/usr/bin/chvt 1");
  249. break;
  250. case RC_HOOK_SERVICE_START_NOW:
  251. if(!ply_message("Starting service", name))
  252. rv = 1;
  253. break;
  254. case RC_HOOK_SERVICE_START_OUT:
  255. /* Start Plymouth daemon if not yet started and tell we have rw access
  256. * to /var/... */
  257. if(strcmp(name, "localmount") == 0 &&
  258. strcmp(runlevel, RC_LEVEL_SHUTDOWN) != 0) {
  259. DBG("ply_update_rootfs_rw()");
  260. if(!ply_update_rootfs_rw())
  261. rv = 1;
  262. }
  263. break;
  264. default:
  265. break;
  266. }
  267. exit:
  268. free(runlevel);
  269. return rv;
  270. }