1 /* Copyright (C) 2020-2022 Sergey Sushilin <sergeysushilin@protonmail.com>
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of either:
6 * the GNU General Public License as published by
7 the Free Software Foundation; version 2.
9 * the GNU General Public License as published by
10 the Free Software Foundation; version 3.
12 or both in parallel, as here.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copies of the GNU General Public License,
20 version 2 and 3 along with this program;
21 if not, see <https://www.gnu.org/licenses/>. */
24 #define _DEFAULT_SOURCE 1
25 #define _FORTIFY_SOURCE 3
39 #include <sys/types.h>
42 #include <sys/socket.h>
52 ACTION_DAEMON_RESTART
,
57 /* Initialized in main(). */
58 static struct passwd
*pw
= NULL
;
59 static uid_t uid
= -1;
60 static char *emacs_directory_name
= NULL
;
61 static char *socket_name
= NULL
;
63 /* Here we do a simple check, whether socket exists,
64 but emacs daemon is not running. This can be when
65 emacs daemon and its watchdog was terminated without
66 allowing to clear up temporary files. */
68 remove_socket_if_not_connectable (void)
70 /* Create new socket. */
71 int sock
= socket (AF_LOCAL
, SOCK_STREAM
, 0);
74 edie (errno
, "socket(AF_LOCAL, SOCK_STREAM, 0)");
76 const size_t socket_name_length
= strlen (socket_name
);
77 struct sockaddr_un server
;
79 if (socket_name_length
>= sizeof (server
.sun_path
))
80 edie (0, "socket name is too long: %s", socket_name
);
82 server
.sun_family
= AF_LOCAL
;
83 memcpy (server
.sun_path
, socket_name
, socket_name_length
+ 1);
85 if (connect (sock
, &server
, sizeof (server
)) < 0 && errno
!= ENOENT
)
86 xunlinkat (AT_FDCWD
, socket_name
);
88 shutdown (sock
, SHUT_RDWR
);
91 static inline __warn_unused_result
bool
92 emacs_daemon_is_running (void)
96 if (unlikely (stat (socket_name
, &sb
) != 0))
98 if (unlikely (errno
!= ENOENT
))
99 edie (errno
, "stat(%s)", socket_name
);
104 if (unlikely (!S_ISSOCK (sb
.st_mode
)))
105 edie (ENOTSOCK
, "%s", socket_name
);
107 /* There is a socket in our directory,
108 but this socket is not owned by us. */
109 if (unlikely (sb
.st_uid
!= uid
))
110 die (EXIT_FAILURE
, "the socket does not belong to us");
112 return likely (faccessat (AT_FDCWD
, socket_name
, X_OK
, AT_EACCESS
) == 0);
115 static inline __returns_nonnull __warn_unused_result
char *
116 get_alternate_editor (void)
118 char *alternate_editor
= getenv ("ALTERNATE_EDITOR");
120 return alternate_editor
!= NULL
? alternate_editor
: (char *) "emacs";
123 static inline __malloc
__nonnull ((1, 3)) __returns_nonnull __warn_unused_result __force_inline
char *
124 catenate (const char *string1
, char delimer
, const char *string2
)
126 size_t length1
= strlen (string1
), length2
= strlen (string2
);
127 char *s
= xmalloc (length1
+ 1 + length2
+ 1);
129 memcpy (&s
[0], string1
, length1
);
130 s
[length1
] = delimer
;
131 memcpy (&s
[length1
+ 1], string2
, length2
);
132 s
[length1
+ 1 + length2
] = '\0';
136 static inline __malloc __returns_nonnull __warn_unused_result
char **
137 get_required_environ (void)
139 /* Actually almost all may-be-used by emacs environ variables. */
140 const char *names
[] =
148 "DBUS_SESSION_BUS_ADDRESS",
195 char **envp
= xmallocarray (countof (names
) + 1, sizeof (char *));
197 for (size_t i
= 0; i
< countof (names
); i
++)
199 const char *name
= names
[i
];
200 const char *value
= getenv (name
);
203 envp
[envn
++] = catenate (name
, '=', value
);
207 return xreallocarray (envp
, envn
+ 1, sizeof (char *));
210 /* Used when starting daemon to make sure
211 that we did not get in infinity waiting. */
212 static __noreturn
void
213 sigaction_sigchld (int signo __unused
,
214 siginfo_t
*restrict info __unused
,
215 void *restrict context __unused
)
217 die (EXIT_FAILURE
, "unexpected emacs daemon death");
220 static inline __nonnull ((3)) void
221 redirect_to_file (int descriptor
, int directory_descriptor
, const char *file
, int flags
, mode_t mode
)
223 /* Redirect descriptor to file at 'directory_descriptor/file'. */
224 int fd
= xopenat (directory_descriptor
, file
, flags
, mode
);
225 xdup2 (fd
, descriptor
);
230 create_emacs_directory (void)
232 /* Access only for its user. */
233 xmkdir (emacs_directory_name
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
236 static inline __warn_unused_result
int
237 get_emacs_directory_descriptor (void)
239 /* Get socket directory descriptor. */
240 return xopen (emacs_directory_name
, O_DIRECTORY
| O_CLOEXEC
, 0);
244 initialize_daemon (void)
246 /* Change default directory to root. */
247 ignore_value (chdir ("/"));
249 create_emacs_directory ();
250 int emacs_directory_descriptor
= get_emacs_directory_descriptor ();
252 /* Redirect stdin to /dev/null for emacs daemon. */
253 redirect_to_file (STDIN_FILENO
, -1, _PATH_DEVNULL
, O_RDONLY
, 0);
255 /* Redirect stdout to /path/to/socket/log for emacs daemon. */
256 redirect_to_file (STDOUT_FILENO
, emacs_directory_descriptor
, "log",
257 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
259 /* Redirect stderr to /path/to/socket/error for emacs daemon. */
260 redirect_to_file (STDERR_FILENO
, emacs_directory_descriptor
, "error",
261 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
263 xclose (emacs_directory_descriptor
);
267 start_daemon (int argc
, char **argv
)
269 struct sigaction new_sighup_action
= { 0 };
271 /* Actually, since we disconnect from controlling terminal, we will not
272 be able to catch such signal, but for daemon sake... */
273 sigemptyset (&new_sighup_action
.sa_mask
);
274 new_sighup_action
.sa_handler
= SIG_IGN
;
275 sigaction (SIGHUP
, &new_sighup_action
, NULL
);
278 pid_t daemonpid
= xfork ();
282 if (program_exited_unsuccessfully (wait_program_termination (daemonpid
)))
283 die (EXIT_FAILURE
, "failed to start daemon");
288 /* Become session leader. */
291 /* Start watchdog. */
292 pid_t watchdogpid
= xfork ();
294 /* Initialize sigaction for SIGCHLD. */
295 struct sigaction new_sigchld_action
= { 0 };
297 sigemptyset (&new_sigchld_action
.sa_mask
);
298 new_sigchld_action
.sa_flags
= SA_NOCLDSTOP
| SA_NOCLDWAIT
| SA_SIGINFO
;
299 new_sigchld_action
.sa_sigaction
= sigaction_sigchld
;
301 /* Set up sigaction for SIGCHLD to find out if someone of our children
303 struct sigaction old_sigchld_action
;
304 sigaction (SIGCHLD
, &new_sigchld_action
, &old_sigchld_action
);
306 if (watchdogpid
!= 0)
308 /* NOTE: this will not degradate to a forever cycle since we set
309 SIGCHLD handler above to die if the daemon failed. */
310 while (!emacs_daemon_is_running ())
311 /* Magic value (40000 microseconds == 0.04 seconds) selected
312 experimentally for two purpuses:
314 1) to do not make user wait too long after daemon actually
315 started but we still sleeping,
316 2) to reduce count of faccessat() (in emacs_daemon_is_running()
317 function) and usleep() syscalls. */
320 /* There is nothing to do more. */
324 /* Restore default behaviour. */
325 sigaction (SIGCHLD
, &old_sigchld_action
, NULL
);
327 initialize_daemon ();
329 /* Start emacs daemon. */
330 pid_t emacspid
= xfork ();
334 if (unlikely (program_exited_unsuccessfully (wait_program_termination (emacspid
))))
335 die (EXIT_FAILURE
, "emacs daemon failed");
342 char **v
= xmallocarray (argc
- 1 + 3, sizeof (*v
));
345 v
[c
++] = catenate ("--fg-daemon", '=', socket_name
);
352 /* 'That's all, folks!' */
353 xexecvpe (v
[0], v
, get_required_environ ());
356 static struct termios attrs
[3];
357 /* Sometimes emacsclient dies unexpectively and do not reset attributes
358 back, so handle this case by ourselves. */
360 restore_attributes (void)
362 /* Restore std{in,out,err} attributes. */
363 xtcsetattr (STDIN_FILENO
, TCSAFLUSH
, &attrs
[0]);
364 xtcsetattr (STDOUT_FILENO
, TCSAFLUSH
, &attrs
[1]);
365 xtcsetattr (STDERR_FILENO
, TCSAFLUSH
, &attrs
[2]);
369 enstore_attributes (void)
371 /* Enstore std{in,out,err} attributes. */
372 xtcgetattr (STDIN_FILENO
, &attrs
[0]);
373 xtcgetattr (STDOUT_FILENO
, &attrs
[1]);
374 xtcgetattr (STDERR_FILENO
, &attrs
[2]);
376 xatexit (restore_attributes
);
380 start_client (int argc
, char **argv
)
382 pid_t pid
= xfork ();
385 return program_exited_successfully (wait_program_termination (pid
)) ? EXIT_SUCCESS
: EXIT_FAILURE
;
389 char **v
= xmallocarray (argc
- 1 + 6, sizeof (*v
));
391 v
[c
++] = "emacsclient";
394 v
[c
++] = catenate ("--socket-name", '=', socket_name
);
395 v
[c
++] = catenate ("--alternate-editor", '=', get_alternate_editor ());
397 /* Copy the rest arguments. */
403 enstore_attributes ();
405 xexecvpe (v
[0], v
, get_required_environ ());
408 /* Called only on daemon stop. */
410 cleanup_emacs_directory (void)
412 int emacs_directory_descriptor
= get_emacs_directory_descriptor ();
414 xunlinkat (emacs_directory_descriptor
, "log");
415 xunlinkat (emacs_directory_descriptor
, "error");
416 xrmdir (emacs_directory_name
);
417 xclose (emacs_directory_descriptor
);
423 pid_t pid
= xfork ();
429 v
[0] = "emacsclient";
430 v
[1] = catenate ("--socket-name", '=', socket_name
);
432 v
[3] = "(kill-emacs)";
435 xexecvpe (v
[0], v
, get_required_environ ());
438 if (unlikely (program_exited_unsuccessfully (wait_program_termination (pid
))))
440 if (!emacs_daemon_is_running ())
441 cleanup_emacs_directory ();
443 die (EXIT_FAILURE
, "failed to stop emacs daemon");
446 cleanup_emacs_directory ();
450 restart_daemon (int argc
, char **argv
)
453 start_daemon (argc
, argv
);
457 load_file (int argc
, char **argv
)
459 pid_t pid
= xfork ();
463 if (unlikely (program_exited_unsuccessfully (wait_program_termination (pid
))))
464 die (EXIT_FAILURE
, "failed to load file in emacs daemon");
470 char **v
= xmallocarray (argc
- 1 + 4 + 1, sizeof (char *));
471 v
[c
++] = "emacsclient";
472 v
[c
++] = catenate ("--socket-name", '=', socket_name
);
475 for (int i
= 1; i
< argc
; i
++)
477 const char load_command
[] = "(load-file \"%s\")";
478 char *command
= xmalloc (sizeof (load_command
) - 2 - 1 + strlen (argv
[i
]) + 1);
480 xsprintf (command
, load_command
, argv
[i
]);
486 xexecvpe (v
[0], v
, get_required_environ ());
489 static inline __noreturn
void
490 usage (const char *argv0
, int status
)
492 fprintf (status
== EXIT_SUCCESS
? stdout
: stderr
, "\
493 Usage: %s [OPTION] FILE...\n\
494 Tiny Emacs Manager.\n\
495 Every FILE can be either just a FILENAME or [+LINE[:COLUMN]] FILENAME.\n\
496 (Except when -load option specified).\n\
498 The following OPTIONS are accepted:\n\
499 -startd Start daemon, arguments passed to started daemon.\n\
500 -stopd Stop daemon, other arguments ignored.\n\
501 -restartd Restart daemon, arguments passed to started daemon.\n\
502 -load Load files specified by [FILE...].\n\
503 -help Print this usage information message.\n\
504 -version Print version.\n\
510 static inline __noreturn
void
513 puts ("Tiny Emacs Manager 1.0");
518 die_if_tty_is_not_connectable (void)
520 /* Do the same emacs does in init_tty() to check whether
521 we are able to connect to a tty. */
523 #if !defined(O_IGNORE_CTTY)
524 # define O_IGNORE_CTTY 0
527 const char *name
= xttyname (STDIN_FILENO
);
528 int fd
= openat (AT_FDCWD
, name
, O_RDWR
| O_NOCTTY
| O_IGNORE_CTTY
, 0);
531 edie (errno
, "%s", name
);
536 static inline enum action
537 parse_options (int *restrict argcp
, char ***restrict argvp
)
540 char **argv
= *argvp
;
541 enum action action
= ACTION_CLIENT_START
;
544 if (unlikely (arg
[0] == '-' && arg
[1] != '\0'))
550 if (streq (arg
, "help"))
551 usage ((*argvp
)[0], EXIT_SUCCESS
);
552 else if (streq (arg
, "version"))
554 else if (streq (arg
, "startd"))
555 action
= ACTION_DAEMON_START
;
556 else if (streq (arg
, "stopd"))
557 action
= ACTION_DAEMON_STOP
;
558 else if (streq (arg
, "restartd"))
559 action
= ACTION_DAEMON_RESTART
;
560 else if (streq (arg
, "load"))
561 action
= ACTION_LOAD_FILE
;
564 fprintf (stderr
, "unknown option %s\n", arg
- 1);
565 usage ((*argvp
)[0], EXIT_FAILURE
);
575 initialize_emacs_directory_name (void)
577 const char emacs_directory_name_format
[] = "%s/.emacs-server";
579 emacs_directory_name
= xmalloc (sizeof (emacs_directory_name_format
) - 1 - 2 + strlen (pw
->pw_dir
) + 1);
580 xsprintf (emacs_directory_name
, emacs_directory_name_format
, pw
->pw_dir
);
584 initialize_socket_name (void)
586 const char socket_name_format
[] = "%s/.emacs-server/socket";
588 socket_name
= xmalloc (sizeof (socket_name_format
) - 1 - 2 + strlen (pw
->pw_dir
) + 1);
589 xsprintf (socket_name
, socket_name_format
, pw
->pw_dir
);
593 main (int argc
, char **argv
)
595 int status
= EXIT_SUCCESS
;
596 enum action action
= ACTION_CLIENT_START
;
598 die_if_tty_is_not_connectable ();
600 pw
= xgetpwuid (uid
);
602 initialize_emacs_directory_name ();
603 initialize_socket_name ();
606 action
= parse_options (&argc
, &argv
);
608 remove_socket_if_not_connectable ();
610 if (unlikely (action
!= ACTION_DAEMON_START
&& !emacs_daemon_is_running ()))
612 /* Little feature to allow user do not specially care about 'e -startd'
613 when he just want to edit file by 'e filename'. */
614 if (action
== ACTION_CLIENT_START
)
615 start_daemon (0, NULL
);
617 die (EXIT_FAILURE
, "emacs daemon is not running");
622 case ACTION_DAEMON_START
:
623 start_daemon (argc
, argv
);
625 case ACTION_DAEMON_STOP
:
628 case ACTION_DAEMON_RESTART
:
629 restart_daemon (argc
, argv
);
631 case ACTION_CLIENT_START
:
632 status
= start_client (argc
, argv
);
634 case ACTION_LOAD_FILE
:
636 die (EXIT_FAILURE
, "at least one file name to load required");
638 load_file (argc
, argv
);