| 
									
										
										
										
											2020-06-11 01:29:07 +01:00
										 |  |  | #include <assert.h>
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | #include <fcntl.h>
 | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | #include <sys/ioctl.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-23 18:00:14 +00:00
										 |  |  | #include <json.h>
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-02 21:41:11 -04:00
										 |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  | #include <sys/wait.h>
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | #include "log.h"
 | 
					
						
							| 
									
										
										
										
											2018-10-13 16:04:37 +10:00
										 |  |  | #include "loop.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-24 21:56:35 +01:00
										 |  |  | #include "swaybar/bar.h"
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | #include "swaybar/config.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-24 21:56:35 +01:00
										 |  |  | #include "swaybar/i3bar.h"
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | #include "swaybar/status_line.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-15 10:15:30 +01:00
										 |  |  | static void status_line_close_fds(struct status_line *status) { | 
					
						
							|  |  |  | 	if (status->read_fd != -1) { | 
					
						
							| 
									
										
										
										
											2018-10-14 12:28:38 +10:00
										 |  |  | 		loop_remove_fd(status->bar->eventloop, status->read_fd); | 
					
						
							| 
									
										
										
										
											2018-09-15 10:15:30 +01:00
										 |  |  | 		close(status->read_fd); | 
					
						
							|  |  |  | 		status->read_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (status->write_fd != -1) { | 
					
						
							|  |  |  | 		close(status->write_fd); | 
					
						
							|  |  |  | 		status->write_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 13:07:22 -04:00
										 |  |  | void status_error(struct status_line *status, const char *text) { | 
					
						
							| 
									
										
										
										
											2018-09-15 10:15:30 +01:00
										 |  |  | 	status_line_close_fds(status); | 
					
						
							| 
									
										
										
										
											2018-03-31 13:07:22 -04:00
										 |  |  | 	status->protocol = PROTOCOL_ERROR; | 
					
						
							|  |  |  | 	status->text = text; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 14:39:18 -04:00
										 |  |  | bool status_handle_readable(struct status_line *status) { | 
					
						
							| 
									
										
										
										
											2018-09-17 13:43:27 +01:00
										 |  |  | 	ssize_t read_bytes = 1; | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 	switch (status->protocol) { | 
					
						
							|  |  |  | 	case PROTOCOL_UNDEF: | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 		errno = 0; | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 		int available_bytes; | 
					
						
							|  |  |  | 		if (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) { | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 			sway_log(SWAY_ERROR, "Unable to read status command output size"); | 
					
						
							| 
									
										
										
										
											2018-03-31 13:07:22 -04:00
										 |  |  | 			status_error(status, "[error reading from status command]"); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 		if ((size_t)available_bytes + 1 > status->buffer_size) { | 
					
						
							|  |  |  | 			// need room for leading '\0' too
 | 
					
						
							|  |  |  | 			status->buffer_size = available_bytes + 1; | 
					
						
							|  |  |  | 			status->buffer = realloc(status->buffer, status->buffer_size); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (status->buffer == NULL) { | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 			sway_log_errno(SWAY_ERROR, "Unable to read status line"); | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 			status_error(status, "[error reading from status command]"); | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		read_bytes = read(status->read_fd, status->buffer, available_bytes); | 
					
						
							|  |  |  | 		if (read_bytes != available_bytes) { | 
					
						
							|  |  |  | 			status_error(status, "[error reading from status command]"); | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		status->buffer[available_bytes] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 		// the header must be sent completely the first time round
 | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 		char *newline = strchr(status->buffer, '\n'); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 		json_object *header, *version; | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 		if (newline != NULL | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 				&& (header = json_tokener_parse(status->buffer)) | 
					
						
							|  |  |  | 				&& json_object_object_get_ex(header, "version", &version) | 
					
						
							|  |  |  | 				&& json_object_get_int(version) == 1) { | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 			sway_log(SWAY_DEBUG, "Using i3bar protocol."); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 			status->protocol = PROTOCOL_I3BAR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			json_object *click_events; | 
					
						
							|  |  |  | 			if (json_object_object_get_ex(header, "click_events", &click_events) | 
					
						
							|  |  |  | 					&& json_object_get_boolean(click_events)) { | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 				sway_log(SWAY_DEBUG, "Enabling click events."); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:10:57 +01:00
										 |  |  | 				status->click_events = true; | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 				if (write(status->write_fd, "[\n", 2) != 2) { | 
					
						
							|  |  |  | 					status_error(status, "[failed to write to status command]"); | 
					
						
							|  |  |  | 					json_object_put(header); | 
					
						
							|  |  |  | 					return true; | 
					
						
							| 
									
										
										
										
											2018-03-31 13:07:22 -04:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-10-12 20:59:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-01 18:08:00 +01:00
										 |  |  | 			json_object *float_event_coords; | 
					
						
							|  |  |  | 			if (json_object_object_get_ex(header, "float_event_coords", &float_event_coords) | 
					
						
							|  |  |  | 					&& json_object_get_boolean(float_event_coords)) { | 
					
						
							|  |  |  | 				sway_log(SWAY_DEBUG, "Enabling floating-point coordinates."); | 
					
						
							|  |  |  | 				status->float_event_coords = true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-12 20:59:45 +01:00
										 |  |  | 			json_object *signal; | 
					
						
							|  |  |  | 			if (json_object_object_get_ex(header, "stop_signal", &signal)) { | 
					
						
							|  |  |  | 				status->stop_signal = json_object_get_int(signal); | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 				sway_log(SWAY_DEBUG, "Setting stop signal to %d", status->stop_signal); | 
					
						
							| 
									
										
										
										
											2018-10-12 20:59:45 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if (json_object_object_get_ex(header, "cont_signal", &signal)) { | 
					
						
							|  |  |  | 				status->cont_signal = json_object_get_int(signal); | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 				sway_log(SWAY_DEBUG, "Setting cont signal to %d", status->cont_signal); | 
					
						
							| 
									
										
										
										
											2018-10-12 20:59:45 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 			json_object_put(header); | 
					
						
							| 
									
										
										
										
											2018-03-31 13:07:22 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 14:39:18 -04:00
										 |  |  | 			wl_list_init(&status->blocks); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:10:57 +01:00
										 |  |  | 			status->tokener = json_tokener_new(); | 
					
						
							| 
									
										
										
										
											2018-09-26 20:38:16 +02:00
										 |  |  | 			status->buffer_index = strlen(newline + 1); | 
					
						
							|  |  |  | 			memmove(status->buffer, newline + 1, status->buffer_index + 1); | 
					
						
							|  |  |  | 			return i3bar_handle_readable(status); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 		sway_log(SWAY_DEBUG, "Using text protocol."); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:00:44 +01:00
										 |  |  | 		status->protocol = PROTOCOL_TEXT; | 
					
						
							|  |  |  | 		status->text = status->buffer; | 
					
						
							|  |  |  | 		// intentional fall-through
 | 
					
						
							| 
									
										
										
										
											2018-09-17 13:43:27 +01:00
										 |  |  | 	case PROTOCOL_TEXT: | 
					
						
							|  |  |  | 		while (true) { | 
					
						
							|  |  |  | 			if (status->buffer[read_bytes - 1] == '\n') { | 
					
						
							|  |  |  | 				status->buffer[read_bytes - 1] = '\0'; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-12-21 12:12:54 +01:00
										 |  |  | 			errno = 0; | 
					
						
							| 
									
										
										
										
											2018-09-17 13:43:27 +01:00
										 |  |  | 			read_bytes = getline(&status->buffer, | 
					
						
							|  |  |  | 					&status->buffer_size, status->read); | 
					
						
							|  |  |  | 			if (errno == EAGAIN) { | 
					
						
							|  |  |  | 				clearerr(status->read); | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			} else if (errno) { | 
					
						
							|  |  |  | 				status_error(status, "[error reading from status command]"); | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-17 14:10:57 +01:00
										 |  |  | 	case PROTOCOL_I3BAR: | 
					
						
							|  |  |  | 		return i3bar_handle_readable(status); | 
					
						
							| 
									
										
										
										
											2018-09-17 13:43:27 +01:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct status_line *status_line_init(char *cmd) { | 
					
						
							|  |  |  | 	struct status_line *status = calloc(1, sizeof(struct status_line)); | 
					
						
							| 
									
										
										
										
											2018-10-12 20:59:45 +01:00
										 |  |  | 	status->stop_signal = SIGSTOP; | 
					
						
							|  |  |  | 	status->cont_signal = SIGCONT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-17 13:43:27 +01:00
										 |  |  | 	status->buffer_size = 8192; | 
					
						
							|  |  |  | 	status->buffer = malloc(status->buffer_size); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int pipe_read_fd[2]; | 
					
						
							|  |  |  | 	int pipe_write_fd[2]; | 
					
						
							|  |  |  | 	if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { | 
					
						
							| 
									
										
										
										
											2019-01-20 13:51:12 -05:00
										 |  |  | 		sway_log(SWAY_ERROR, "Unable to create pipes for status_command fork"); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 01:29:07 +01:00
										 |  |  | 	assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " | 
					
						
							|  |  |  | 		" starting `status-command`; WAYLAND_SOCKET should not be set"); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 	status->pid = fork(); | 
					
						
							| 
									
										
										
										
											2021-11-21 10:55:20 +01:00
										 |  |  | 	if (status->pid < 0) { | 
					
						
							|  |  |  | 		sway_log_errno(SWAY_ERROR, "fork failed"); | 
					
						
							|  |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} else if (status->pid == 0) { | 
					
						
							|  |  |  | 		setpgid(0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | 		dup2(pipe_read_fd[1], STDOUT_FILENO); | 
					
						
							|  |  |  | 		close(pipe_read_fd[0]); | 
					
						
							|  |  |  | 		close(pipe_read_fd[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dup2(pipe_write_fd[0], STDIN_FILENO); | 
					
						
							|  |  |  | 		close(pipe_write_fd[0]); | 
					
						
							|  |  |  | 		close(pipe_write_fd[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		char *const _cmd[] = { "sh", "-c", cmd, NULL, }; | 
					
						
							|  |  |  | 		execvp(_cmd[0], _cmd); | 
					
						
							|  |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	close(pipe_read_fd[1]); | 
					
						
							|  |  |  | 	status->read_fd = pipe_read_fd[0]; | 
					
						
							|  |  |  | 	fcntl(status->read_fd, F_SETFL, O_NONBLOCK); | 
					
						
							|  |  |  | 	close(pipe_write_fd[0]); | 
					
						
							|  |  |  | 	status->write_fd = pipe_write_fd[1]; | 
					
						
							|  |  |  | 	fcntl(status->write_fd, F_SETFL, O_NONBLOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status->read = fdopen(status->read_fd, "r"); | 
					
						
							|  |  |  | 	status->write = fdopen(status->write_fd, "w"); | 
					
						
							|  |  |  | 	return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 15:19:42 -04:00
										 |  |  | void status_line_free(struct status_line *status) { | 
					
						
							| 
									
										
										
										
											2018-09-15 10:15:30 +01:00
										 |  |  | 	status_line_close_fds(status); | 
					
						
							| 
									
										
										
										
											2021-11-21 10:55:20 +01:00
										 |  |  | 	kill(-status->pid, status->cont_signal); | 
					
						
							|  |  |  | 	kill(-status->pid, SIGTERM); | 
					
						
							| 
									
										
										
										
											2019-09-02 21:41:11 -04:00
										 |  |  | 	waitpid(status->pid, NULL, 0); | 
					
						
							| 
									
										
										
										
											2018-09-17 14:10:57 +01:00
										 |  |  | 	if (status->protocol == PROTOCOL_I3BAR) { | 
					
						
							| 
									
										
										
										
											2018-04-24 22:04:19 +01:00
										 |  |  | 		struct i3bar_block *block, *tmp; | 
					
						
							|  |  |  | 		wl_list_for_each_safe(block, tmp, &status->blocks, link) { | 
					
						
							| 
									
										
										
										
											2018-09-15 10:14:21 +01:00
										 |  |  | 			wl_list_remove(&block->link); | 
					
						
							| 
									
										
										
										
											2018-09-12 08:28:28 +01:00
										 |  |  | 			i3bar_block_unref(block); | 
					
						
							| 
									
										
										
										
											2018-04-24 22:04:19 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-18 18:06:19 +01:00
										 |  |  | 		json_tokener_free(status->tokener); | 
					
						
							| 
									
										
										
										
											2018-04-24 22:04:19 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-17 14:10:57 +01:00
										 |  |  | 	free(status->buffer); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:19:42 -04:00
										 |  |  | 	free(status); | 
					
						
							| 
									
										
										
										
											2018-03-29 15:16:12 -04:00
										 |  |  | } |