Bump liberty
This commit is contained in:
		
							parent
							
								
									8e792c4f4d
								
							
						
					
					
						commit
						644d55476f
					
				
							
								
								
									
										39
									
								
								brightness.c
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								brightness.c
									
									
									
									
									
								
							| @ -55,12 +55,6 @@ log_message_custom (void *user_data, const char *quote, const char *fmt, | ||||
| 	fputs ("\n", stream); | ||||
| } | ||||
| 
 | ||||
| #define FAIL(...)                                                              \ | ||||
| 	BLOCK_START                                                                \ | ||||
| 		error_set (e, __VA_ARGS__);                                            \ | ||||
| 		return false;                                                          \ | ||||
| 	BLOCK_END | ||||
| 
 | ||||
| static void | ||||
| wait_ms (long ms) | ||||
| { | ||||
| @ -122,9 +116,9 @@ check_edid (int fd, struct error **e) | ||||
| 	data.nmsgs = 2; | ||||
| 
 | ||||
| 	if (ioctl (fd, I2C_RDWR, &data) < 0) | ||||
| 		FAIL ("%s: %s", "ioctl", strerror (errno)); | ||||
| 		return error_set (e, "%s: %s", "ioctl", strerror (errno)); | ||||
| 	if (memcmp ("\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", buf, sizeof buf)) | ||||
| 		FAIL ("invalid EDID"); | ||||
| 		return error_set (e, "invalid EDID"); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| @ -133,13 +127,13 @@ is_a_display (int fd, struct error **e) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	if (fstat (fd, &st) < 0) | ||||
| 		FAIL ("%s: %s", "fstat", strerror (errno)); | ||||
| 		return error_set (e, "%s: %s", "fstat", strerror (errno)); | ||||
| 
 | ||||
| 	unsigned long funcs; | ||||
| 	if (!(st.st_mode & S_IFCHR) | ||||
| 	 || ioctl (fd, I2C_FUNCS, &funcs) < 0 | ||||
| 	 || !(funcs & I2C_FUNC_I2C)) | ||||
| 		FAIL ("not an I2C device"); | ||||
| 		return error_set (e, "not an I2C device"); | ||||
| 
 | ||||
| 	return check_edid (fd, e); | ||||
| } | ||||
| @ -176,7 +170,7 @@ ddc_send (int fd, unsigned command, void *args, size_t args_len, | ||||
| 	bool failed = ioctl (fd, I2C_RDWR, &data) < 0; | ||||
| 	str_free (&buf); | ||||
| 	if (failed) | ||||
| 		FAIL ("%s: %s", "ioctl", strerror (errno)); | ||||
| 		return error_set (e, "%s: %s", "ioctl", strerror (errno)); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| @ -197,7 +191,7 @@ ddc_read (int fd, unsigned *command, void *out_buf, size_t *n_read, | ||||
| 	data.nmsgs = 1; | ||||
| 
 | ||||
| 	if (ioctl (fd, I2C_RDWR, &data) < 0) | ||||
| 		FAIL ("%s: %s", "ioctl", strerror (errno)); | ||||
| 		return error_set (e, "%s: %s", "ioctl", strerror (errno)); | ||||
| 
 | ||||
| 	struct msg_unpacker unpacker; | ||||
| 	msg_unpacker_init (&unpacker, buf, sizeof buf); | ||||
| @ -208,9 +202,9 @@ ddc_read (int fd, unsigned *command, void *out_buf, size_t *n_read, | ||||
| 	(void) msg_unpacker_u8 (&unpacker, &cmd); | ||||
| 
 | ||||
| 	if (sender != (DDC_ADDRESS_DISPLAY | I2C_WRITE) || !(length & 0x80)) | ||||
| 		FAIL ("invalid response"); | ||||
| 		return error_set (e, "invalid response"); | ||||
| 	if (!(length ^= 0x80)) | ||||
| 		FAIL ("NULL response"); | ||||
| 		return error_set (e, "NULL response"); | ||||
| 
 | ||||
| 	// TODO: also check the checksum
 | ||||
| 
 | ||||
| @ -237,7 +231,7 @@ set_brightness (int fd, long diff, struct error **e) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (command != DDC_GET_VCP_FEATURE_REPLY || len != 7) | ||||
| 		FAIL ("invalid response"); | ||||
| 		return error_set (e, "invalid response"); | ||||
| 
 | ||||
| 	struct msg_unpacker unpacker; | ||||
| 	msg_unpacker_init (&unpacker, buf, len); | ||||
| @ -249,15 +243,15 @@ set_brightness (int fd, long diff, struct error **e) | ||||
| 	int16_t cur;        msg_unpacker_i16 (&unpacker, &cur); | ||||
| 
 | ||||
| 	if (result == 0x01) | ||||
| 		FAIL ("error reported by monitor"); | ||||
| 		return error_set (e, "error reported by monitor"); | ||||
| 
 | ||||
| 	if (result != 0x00 | ||||
| 	 || vcp_opcode != VCP_BRIGHTNESS) | ||||
| 		FAIL ("invalid response"); | ||||
| 		return error_set (e, "invalid response"); | ||||
| 
 | ||||
| 	// These are unsigned but usually just one byte long
 | ||||
| 	if (max < 0 || cur < 0) | ||||
| 		FAIL ("capability range overflow"); | ||||
| 		return error_set (e, "capability range overflow"); | ||||
| 
 | ||||
| 	int16_t req = (cur * 100 + diff * max + 50) / 100; | ||||
| 	if (req > max) req = max; | ||||
| @ -360,10 +354,7 @@ set_backlight (int dir, long diff, struct error **e) | ||||
| 	} | ||||
| 
 | ||||
| 	if (cur < 0 || max < 0) | ||||
| 	{ | ||||
| 		error_set (e, "invalid range or current value"); | ||||
| 		return false; | ||||
| 	} | ||||
| 		return error_set (e, "invalid range or current value"); | ||||
| 
 | ||||
| 	long req = (cur * 100 + diff * max + 50) / 100; | ||||
| 	if (req > max) req = max; | ||||
| @ -372,8 +363,8 @@ set_backlight (int dir, long diff, struct error **e) | ||||
| 	int fd = openat (dir, "brightness", O_WRONLY); | ||||
| 	if (fd < 0) | ||||
| 	{ | ||||
| 		error_set (e, "%s: %s: %s", "brightness", "openat", strerror (errno)); | ||||
| 		return false; | ||||
| 		return error_set (e, | ||||
| 			"%s: %s: %s", "brightness", "openat", strerror (errno)); | ||||
| 	} | ||||
| 
 | ||||
| 	struct str s; | ||||
|  | ||||
							
								
								
									
										720
									
								
								dwmstatus.c
									
									
									
									
									
								
							
							
						
						
									
										720
									
								
								dwmstatus.c
									
									
									
									
									
								
							| @ -19,6 +19,7 @@ | ||||
| 
 | ||||
| #define LIBERTY_WANT_POLLER | ||||
| #define LIBERTY_WANT_ASYNC | ||||
| #define LIBERTY_WANT_PROTO_MPD | ||||
| 
 | ||||
| #define _GNU_SOURCE // openat
 | ||||
| 
 | ||||
| @ -28,7 +29,6 @@ | ||||
| #include "liberty/liberty.c" | ||||
| 
 | ||||
| #include <dirent.h> | ||||
| #include <sys/un.h> | ||||
| #include <spawn.h> | ||||
| 
 | ||||
| #include <X11/Xlib.h> | ||||
| @ -65,67 +65,6 @@ set_dwm_status (Display *dpy, const char *str) | ||||
| 	XSync (dpy, False); | ||||
| } | ||||
| 
 | ||||
| // --- Simple network I/O ------------------------------------------------------
 | ||||
| 
 | ||||
| enum socket_io_result | ||||
| { | ||||
| 	SOCKET_IO_OK = 0,                   ///< Completed successfully
 | ||||
| 	SOCKET_IO_EOF,                      ///< Connection shut down by peer
 | ||||
| 	SOCKET_IO_ERROR                     ///< Connection error
 | ||||
| }; | ||||
| 
 | ||||
| static enum socket_io_result | ||||
| socket_io_try_read (int socket_fd, struct str *rb) | ||||
| { | ||||
| 	ssize_t n_read; | ||||
| 	while (true) | ||||
| 	{ | ||||
| 		str_ensure_space (rb, 512); | ||||
| 		n_read = recv (socket_fd, rb->str + rb->len, | ||||
| 			rb->alloc - rb->len - 1 /* null byte */, 0); | ||||
| 
 | ||||
| 		if (n_read > 0) | ||||
| 		{ | ||||
| 			rb->str[rb->len += n_read] = '\0'; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (n_read == 0) | ||||
| 			return SOCKET_IO_EOF; | ||||
| 
 | ||||
| 		if (errno == EAGAIN) | ||||
| 			return SOCKET_IO_OK; | ||||
| 		if (errno == EINTR) | ||||
| 			continue; | ||||
| 
 | ||||
| 		LOG_LIBC_FAILURE ("recv"); | ||||
| 		return SOCKET_IO_ERROR; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static enum socket_io_result | ||||
| socket_io_try_write (int socket_fd, struct str *wb) | ||||
| { | ||||
| 	ssize_t n_written; | ||||
| 	while (wb->len) | ||||
| 	{ | ||||
| 		n_written = send (socket_fd, wb->str, wb->len, 0); | ||||
| 		if (n_written >= 0) | ||||
| 		{ | ||||
| 			str_remove_slice (wb, 0, n_written); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (errno == EAGAIN) | ||||
| 			return SOCKET_IO_OK; | ||||
| 		if (errno == EINTR) | ||||
| 			continue; | ||||
| 
 | ||||
| 		LOG_LIBC_FAILURE ("send"); | ||||
| 		return SOCKET_IO_ERROR; | ||||
| 	} | ||||
| 	return SOCKET_IO_OK; | ||||
| } | ||||
| 
 | ||||
| // --- PulseAudio mainloop abstraction -----------------------------------------
 | ||||
| 
 | ||||
| struct pa_io_event | ||||
| @ -467,650 +406,6 @@ poller_pa_run (struct pa_mainloop_api *api) | ||||
| 	return data->result; | ||||
| } | ||||
| 
 | ||||
| // --- MPD client interface ----------------------------------------------------
 | ||||
| 
 | ||||
| // This is a rather thin MPD client interface intended for basic tasks
 | ||||
| 
 | ||||
| #define MPD_SUBSYSTEM_TABLE(XX)                 \ | ||||
| 	XX (DATABASE,         0, "database")        \ | ||||
| 	XX (UPDATE,           1, "update")          \ | ||||
| 	XX (STORED_PLAYLIST,  2, "stored_playlist") \ | ||||
| 	XX (PLAYLIST,         3, "playlist")        \ | ||||
| 	XX (PLAYER,           4, "player")          \ | ||||
| 	XX (MIXER,            5, "mixer")           \ | ||||
| 	XX (OUTPUT,           6, "output")          \ | ||||
| 	XX (OPTIONS,          7, "options")         \ | ||||
| 	XX (STICKER,          8, "sticker")         \ | ||||
| 	XX (SUBSCRIPTION,     9, "subscription")    \ | ||||
| 	XX (MESSAGE,         10, "message") | ||||
| 
 | ||||
| enum mpd_subsystem | ||||
| { | ||||
| #define XX(a, b, c) MPD_SUBSYSTEM_ ## a = (1 << b), | ||||
| 	MPD_SUBSYSTEM_TABLE (XX) | ||||
| #undef XX | ||||
| 	MPD_SUBSYSTEM_MAX | ||||
| }; | ||||
| 
 | ||||
| static const char *mpd_subsystem_names[] = | ||||
| { | ||||
| #define XX(a, b, c) [b] = c, | ||||
| 	MPD_SUBSYSTEM_TABLE (XX) | ||||
| #undef XX | ||||
| }; | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| enum mpd_client_state | ||||
| { | ||||
| 	MPD_DISCONNECTED,                   ///< Not connected
 | ||||
| 	MPD_CONNECTING,                     ///< Currently connecting
 | ||||
| 	MPD_CONNECTED                       ///< Connected
 | ||||
| }; | ||||
| 
 | ||||
| struct mpd_response | ||||
| { | ||||
| 	bool success;                       ///< OK or ACK
 | ||||
| 
 | ||||
| 	// ACK-only fields:
 | ||||
| 
 | ||||
| 	int error;                          ///< Numeric error value (ack.h)
 | ||||
| 	int list_offset;                    ///< Offset of command in list
 | ||||
| 	char *current_command;              ///< Name of the erroring command
 | ||||
| 	char *message_text;                 ///< Error message
 | ||||
| }; | ||||
| 
 | ||||
| /// Task completion callback
 | ||||
| typedef void (*mpd_client_task_cb) (const struct mpd_response *response, | ||||
| 	const struct str_vector *data, void *user_data); | ||||
| 
 | ||||
| struct mpd_client_task | ||||
| { | ||||
| 	LIST_HEADER (struct mpd_client_task) | ||||
| 
 | ||||
| 	mpd_client_task_cb callback;        ///< Callback on completion
 | ||||
| 	void *user_data;                    ///< User data
 | ||||
| }; | ||||
| 
 | ||||
| struct mpd_client | ||||
| { | ||||
| 	struct poller *poller;              ///< Poller
 | ||||
| 
 | ||||
| 	// Connection:
 | ||||
| 
 | ||||
| 	enum mpd_client_state state;        ///< Connection state
 | ||||
| 	struct connector *connector;        ///< Connection establisher
 | ||||
| 
 | ||||
| 	int socket;                         ///< MPD socket
 | ||||
| 	struct str read_buffer;             ///< Input yet to be processed
 | ||||
| 	struct str write_buffer;            ///< Outut yet to be be sent out
 | ||||
| 	struct poller_fd socket_event;      ///< We can read from the socket
 | ||||
| 
 | ||||
| 	struct poller_timer timeout_timer;  ///< Connection seems to be dead
 | ||||
| 
 | ||||
| 	// Protocol:
 | ||||
| 
 | ||||
| 	bool got_hello;                     ///< Got the OK MPD hello message
 | ||||
| 
 | ||||
| 	bool idling;                        ///< Sent idle as the last command
 | ||||
| 	unsigned idling_subsystems;         ///< Subsystems we're idling for
 | ||||
| 	bool in_list;                       ///< We're inside a command list
 | ||||
| 
 | ||||
| 	struct mpd_client_task *tasks;      ///< Task queue
 | ||||
| 	struct mpd_client_task *tasks_tail; ///< Tail of task queue
 | ||||
| 	struct str_vector data;             ///< Data from last command
 | ||||
| 
 | ||||
| 	// User configuration:
 | ||||
| 
 | ||||
| 	void *user_data;                    ///< User data for callbacks
 | ||||
| 
 | ||||
| 	/// Callback after connection has been successfully established
 | ||||
| 	void (*on_connected) (void *user_data); | ||||
| 
 | ||||
| 	/// Callback for general failures or even normal disconnection;
 | ||||
| 	/// the interface is reinitialized
 | ||||
| 	void (*on_failure) (void *user_data); | ||||
| 
 | ||||
| 	/// Callback to receive "idle" updates.
 | ||||
| 	/// Remember to restart the idle if needed.
 | ||||
| 	void (*on_event) (unsigned subsystems, void *user_data); | ||||
| }; | ||||
| 
 | ||||
| static void mpd_client_reset (struct mpd_client *self); | ||||
| static void mpd_client_destroy_connector (struct mpd_client *self); | ||||
| 
 | ||||
| static void | ||||
| mpd_client_init (struct mpd_client *self, struct poller *poller) | ||||
| { | ||||
| 	memset (self, 0, sizeof *self); | ||||
| 
 | ||||
| 	self->poller = poller; | ||||
| 	self->socket = -1; | ||||
| 
 | ||||
| 	str_init (&self->read_buffer); | ||||
| 	str_init (&self->write_buffer); | ||||
| 
 | ||||
| 	str_vector_init (&self->data); | ||||
| 
 | ||||
| 	poller_fd_init (&self->socket_event, poller, -1); | ||||
| 	poller_timer_init (&self->timeout_timer, poller); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_free (struct mpd_client *self) | ||||
| { | ||||
| 	// So that we don't have to repeat most of the stuff
 | ||||
| 	mpd_client_reset (self); | ||||
| 
 | ||||
| 	str_free (&self->read_buffer); | ||||
| 	str_free (&self->write_buffer); | ||||
| 
 | ||||
| 	str_vector_free (&self->data); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| /// Reinitialize the interface so that you can reconnect anew
 | ||||
| static void | ||||
| mpd_client_reset (struct mpd_client *self) | ||||
| { | ||||
| 	if (self->state == MPD_CONNECTING) | ||||
| 		mpd_client_destroy_connector (self); | ||||
| 
 | ||||
| 	if (self->socket != -1) | ||||
| 		xclose (self->socket); | ||||
| 	self->socket = -1; | ||||
| 
 | ||||
| 	self->socket_event.closed = true; | ||||
| 	poller_fd_reset (&self->socket_event); | ||||
| 	poller_timer_reset (&self->timeout_timer); | ||||
| 
 | ||||
| 	str_reset (&self->read_buffer); | ||||
| 	str_reset (&self->write_buffer); | ||||
| 
 | ||||
| 	str_vector_reset (&self->data); | ||||
| 
 | ||||
| 	self->got_hello = false; | ||||
| 	self->idling = false; | ||||
| 	self->idling_subsystems = 0; | ||||
| 	self->in_list = false; | ||||
| 
 | ||||
| 	LIST_FOR_EACH (struct mpd_client_task, iter, self->tasks) | ||||
| 		free (iter); | ||||
| 	self->tasks = self->tasks_tail = NULL; | ||||
| 
 | ||||
| 	self->state = MPD_DISCONNECTED; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_fail (struct mpd_client *self) | ||||
| { | ||||
| 	mpd_client_reset (self); | ||||
| 	if (self->on_failure) | ||||
| 		self->on_failure (self->user_data); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_parse_response (const char *p, struct mpd_response *response) | ||||
| { | ||||
| 	if (!strcmp (p, "OK")) | ||||
| 		return response->success = true; | ||||
| 	if (!strcmp (p, "list_OK")) | ||||
| 		// TODO: either implement this or fail the connection properly
 | ||||
| 		hard_assert (!"command_list_ok_begin not implemented"); | ||||
| 
 | ||||
| 	char *end = NULL; | ||||
| 	if (*p++ != 'A' || *p++ != 'C' || *p++ != 'K' || *p++ != ' ' || *p++ != '[') | ||||
| 		return false; | ||||
| 
 | ||||
| 	errno = 0; | ||||
| 	response->error = strtoul (p, &end, 10); | ||||
| 	if (errno != 0 || end == p) | ||||
| 		return false; | ||||
| 	p = end; | ||||
| 	if (*p++ != '@') | ||||
| 		return false; | ||||
| 
 | ||||
| 	errno = 0; | ||||
| 	response->list_offset = strtoul (p, &end, 10); | ||||
| 	if (errno != 0 || end == p) | ||||
| 		return false; | ||||
| 	p = end; | ||||
| 	if (*p++ != ']' || *p++ != ' ' || *p++ != '{' || !(end = strchr (p, '}'))) | ||||
| 		return false; | ||||
| 
 | ||||
| 	response->current_command = xstrndup (p, end - p); | ||||
| 	p = end + 1; | ||||
| 
 | ||||
| 	if (*p++ != ' ') | ||||
| 		return false; | ||||
| 
 | ||||
| 	response->message_text = xstrdup (p); | ||||
| 	response->success = false; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_dispatch (struct mpd_client *self, struct mpd_response *response) | ||||
| { | ||||
| 	struct mpd_client_task *task; | ||||
| 	if (!(task = self->tasks)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (task->callback) | ||||
| 		task->callback (response, &self->data, task->user_data); | ||||
| 	str_vector_reset (&self->data); | ||||
| 
 | ||||
| 	LIST_UNLINK_WITH_TAIL (self->tasks, self->tasks_tail, task); | ||||
| 	free (task); | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_parse_hello (struct mpd_client *self, const char *line) | ||||
| { | ||||
| 	const char hello[] = "OK MPD "; | ||||
| 	if (strncmp (line, hello, sizeof hello - 1)) | ||||
| 	{ | ||||
| 		print_debug ("invalid MPD hello message"); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO: call "on_connected" now.  We should however also set up a timer
 | ||||
| 	//   so that we don't wait on this message forever.
 | ||||
| 	return self->got_hello = true; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_parse_line (struct mpd_client *self, const char *line) | ||||
| { | ||||
| 	print_debug ("MPD >> %s", line); | ||||
| 
 | ||||
| 	if (!self->got_hello) | ||||
| 		return mpd_client_parse_hello (self, line); | ||||
| 
 | ||||
| 	struct mpd_response response; | ||||
| 	memset (&response, 0, sizeof response); | ||||
| 	if (mpd_client_parse_response (line, &response)) | ||||
| 	{ | ||||
| 		mpd_client_dispatch (self, &response); | ||||
| 		free (response.current_command); | ||||
| 		free (response.message_text); | ||||
| 	} | ||||
| 	else | ||||
| 		str_vector_add (&self->data, line); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /// All output from MPD commands seems to be in a trivial "key: value" format
 | ||||
| static char * | ||||
| mpd_client_parse_kv (char *line, char **value) | ||||
| { | ||||
| 	char *sep; | ||||
| 	if (!(sep = strstr (line, ": "))) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	*sep = 0; | ||||
| 	*value = sep + 2; | ||||
| 	return line; | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static void | ||||
| mpd_client_update_poller (struct mpd_client *self) | ||||
| { | ||||
| 	poller_fd_set (&self->socket_event, | ||||
| 		self->write_buffer.len ? (POLLIN | POLLOUT) : POLLIN); | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_process_input (struct mpd_client *self) | ||||
| { | ||||
| 	// Split socket input at newlines and process them separately
 | ||||
| 	struct str *rb = &self->read_buffer; | ||||
| 	char *start = rb->str, *end = start + rb->len; | ||||
| 	for (char *p = start; p < end; p++) | ||||
| 	{ | ||||
| 		if (*p != '\n') | ||||
| 			continue; | ||||
| 
 | ||||
| 		*p = 0; | ||||
| 		if (!mpd_client_parse_line (self, start)) | ||||
| 			return false; | ||||
| 		start = p + 1; | ||||
| 	} | ||||
| 
 | ||||
| 	str_remove_slice (rb, 0, start - rb->str); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_on_ready (const struct pollfd *pfd, void *user_data) | ||||
| { | ||||
| 	(void) pfd; | ||||
| 
 | ||||
| 	struct mpd_client *self = user_data; | ||||
| 	if (socket_io_try_read  (self->socket, &self->read_buffer)  != SOCKET_IO_OK | ||||
| 	 || !mpd_client_process_input (self) | ||||
| 	 || socket_io_try_write (self->socket, &self->write_buffer) != SOCKET_IO_OK) | ||||
| 		mpd_client_fail (self); | ||||
| 	else | ||||
| 		mpd_client_update_poller (self); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_must_quote_char (char c) | ||||
| { | ||||
| 	return (unsigned char) c <= ' ' || c == '"' || c == '\''; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_must_quote (const char *s) | ||||
| { | ||||
| 	if (!*s) | ||||
| 		return true; | ||||
| 	for (; *s; s++) | ||||
| 		if (mpd_client_must_quote_char (*s)) | ||||
| 			return true; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_quote (const char *s, struct str *output) | ||||
| { | ||||
| 	str_append_c (output, '"'); | ||||
| 	for (; *s; s++) | ||||
| 	{ | ||||
| 		if (mpd_client_must_quote_char (*s)) | ||||
| 			str_append_c (output, '\\'); | ||||
| 		str_append_c (output, *s); | ||||
| 	} | ||||
| 	str_append_c (output, '"'); | ||||
| } | ||||
| 
 | ||||
| /// Beware that delivery of the event isn't deferred and you musn't make
 | ||||
| /// changes to the interface while processing the event!
 | ||||
| static void | ||||
| mpd_client_add_task | ||||
| 	(struct mpd_client *self, mpd_client_task_cb cb, void *user_data) | ||||
| { | ||||
| 	// This only has meaning with command_list_ok_begin, and then it requires
 | ||||
| 	// special handling (all in-list tasks need to be specially marked and
 | ||||
| 	// later flushed if an early ACK or OK arrives).
 | ||||
| 	hard_assert (!self->in_list); | ||||
| 
 | ||||
| 	struct mpd_client_task *task = xcalloc (1, sizeof *self); | ||||
| 	task->callback = cb; | ||||
| 	task->user_data = user_data; | ||||
| 	LIST_APPEND_WITH_TAIL (self->tasks, self->tasks_tail, task); | ||||
| } | ||||
| 
 | ||||
| /// Send a command.  Remember to call mpd_client_add_task() to handle responses,
 | ||||
| /// unless the command is being sent in a list.
 | ||||
| static void mpd_client_send_command | ||||
| 	(struct mpd_client *self, const char *command, ...) ATTRIBUTE_SENTINEL; | ||||
| 
 | ||||
| static void | ||||
| mpd_client_send_commandv (struct mpd_client *self, char **commands) | ||||
| { | ||||
| 	// Automatically interrupt idle mode
 | ||||
| 	if (self->idling) | ||||
| 	{ | ||||
| 		poller_timer_reset (&self->timeout_timer); | ||||
| 
 | ||||
| 		self->idling = false; | ||||
| 		self->idling_subsystems = 0; | ||||
| 		mpd_client_send_command (self, "noidle", NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	struct str line; | ||||
| 	str_init (&line); | ||||
| 
 | ||||
| 	for (; *commands; commands++) | ||||
| 	{ | ||||
| 		if (line.len) | ||||
| 			str_append_c (&line, ' '); | ||||
| 
 | ||||
| 		if (mpd_client_must_quote (*commands)) | ||||
| 			mpd_client_quote (*commands, &line); | ||||
| 		else | ||||
| 			str_append (&line, *commands); | ||||
| 	} | ||||
| 
 | ||||
| 	print_debug ("MPD << %s", line.str); | ||||
| 	str_append_c (&line, '\n'); | ||||
| 	str_append_str (&self->write_buffer, &line); | ||||
| 	str_free (&line); | ||||
| 
 | ||||
| 	mpd_client_update_poller (self); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_send_command (struct mpd_client *self, const char *command, ...) | ||||
| { | ||||
| 	struct str_vector v; | ||||
| 	str_vector_init (&v); | ||||
| 
 | ||||
| 	va_list ap; | ||||
| 	va_start (ap, command); | ||||
| 	for (; command; command = va_arg (ap, const char *)) | ||||
| 		str_vector_add (&v, command); | ||||
| 	va_end (ap); | ||||
| 
 | ||||
| 	mpd_client_send_commandv (self, v.vector); | ||||
| 	str_vector_free (&v); | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static void | ||||
| mpd_client_list_begin (struct mpd_client *self) | ||||
| { | ||||
| 	hard_assert (!self->in_list); | ||||
| 	mpd_client_send_command (self, "command_list_begin", NULL); | ||||
| 	self->in_list = true; | ||||
| } | ||||
| 
 | ||||
| /// End a list of commands.  Remember to call mpd_client_add_task()
 | ||||
| /// to handle the summary response.
 | ||||
| static void | ||||
| mpd_client_list_end (struct mpd_client *self) | ||||
| { | ||||
| 	hard_assert (self->in_list); | ||||
| 	mpd_client_send_command (self, "command_list_end", NULL); | ||||
| 	self->in_list = false; | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static bool | ||||
| mpd_resolve_subsystem (const char *name, unsigned *output) | ||||
| { | ||||
| 	for (size_t i = 0; i < N_ELEMENTS (mpd_subsystem_names); i++) | ||||
| 		if (!strcasecmp_ascii (name, mpd_subsystem_names[i])) | ||||
| 		{ | ||||
| 			*output |= 1 << i; | ||||
| 			return true; | ||||
| 		} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_on_idle_return (const struct mpd_response *response, | ||||
| 	const struct str_vector *data, void *user_data) | ||||
| { | ||||
| 	(void) response; | ||||
| 
 | ||||
| 	struct mpd_client *self = user_data; | ||||
| 	unsigned subsystems = 0; | ||||
| 	for (size_t i = 0; i < data->len; i++) | ||||
| 	{ | ||||
| 		char *value, *key; | ||||
| 		if (!(key = mpd_client_parse_kv (data->vector[i], &value))) | ||||
| 			print_debug ("%s: %s", "erroneous MPD output", data->vector[i]); | ||||
| 		else if (strcasecmp_ascii (key, "changed")) | ||||
| 			print_debug ("%s: %s", "unexpected idle key", key); | ||||
| 		else if (!mpd_resolve_subsystem (value, &subsystems)) | ||||
| 			print_debug ("%s: %s", "unknown subsystem", value); | ||||
| 	} | ||||
| 
 | ||||
| 	// Not resetting "idling" here, we may send an extra "noidle" no problem
 | ||||
| 	if (self->on_event && subsystems) | ||||
| 		self->on_event (subsystems, self->user_data); | ||||
| } | ||||
| 
 | ||||
| static void mpd_client_idle (struct mpd_client *self, unsigned subsystems); | ||||
| 
 | ||||
| static void | ||||
| mpd_client_on_timeout (void *user_data) | ||||
| { | ||||
| 	struct mpd_client *self = user_data; | ||||
| 	unsigned subsystems = self->idling_subsystems; | ||||
| 
 | ||||
| 	// Just sending this out should bring a dead connection down over TCP
 | ||||
| 	// TODO: set another timer to make sure the ping reply arrives
 | ||||
| 	mpd_client_send_command (self, "ping", NULL); | ||||
| 	mpd_client_add_task (self, NULL, NULL); | ||||
| 
 | ||||
| 	// Restore the incriminating idle immediately
 | ||||
| 	mpd_client_idle (self, subsystems); | ||||
| } | ||||
| 
 | ||||
| /// When not expecting to send any further commands, you should call this
 | ||||
| /// in order to keep the connection alive.  Or to receive updates.
 | ||||
| static void | ||||
| mpd_client_idle (struct mpd_client *self, unsigned subsystems) | ||||
| { | ||||
| 	hard_assert (!self->in_list); | ||||
| 
 | ||||
| 	struct str_vector v; | ||||
| 	str_vector_init (&v); | ||||
| 
 | ||||
| 	str_vector_add (&v, "idle"); | ||||
| 	for (size_t i = 0; i < N_ELEMENTS (mpd_subsystem_names); i++) | ||||
| 		if (subsystems & (1 << i)) | ||||
| 			str_vector_add (&v, mpd_subsystem_names[i]); | ||||
| 
 | ||||
| 	mpd_client_send_commandv (self, v.vector); | ||||
| 	str_vector_free (&v); | ||||
| 
 | ||||
| 	self->timeout_timer.dispatcher = mpd_client_on_timeout; | ||||
| 	self->timeout_timer.user_data = self; | ||||
| 	poller_timer_set (&self->timeout_timer, 5 * 60 * 1000); | ||||
| 
 | ||||
| 	mpd_client_add_task (self, mpd_client_on_idle_return, self); | ||||
| 	self->idling = true; | ||||
| 	self->idling_subsystems = subsystems; | ||||
| } | ||||
| 
 | ||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||
| 
 | ||||
| static void | ||||
| mpd_client_finish_connection (struct mpd_client *self, int socket) | ||||
| { | ||||
| 	set_blocking (socket, false); | ||||
| 	self->socket = socket; | ||||
| 	self->state = MPD_CONNECTED; | ||||
| 
 | ||||
| 	poller_fd_init (&self->socket_event, self->poller, self->socket); | ||||
| 	self->socket_event.dispatcher = mpd_client_on_ready; | ||||
| 	self->socket_event.user_data = self; | ||||
| 
 | ||||
| 	mpd_client_update_poller (self); | ||||
| 
 | ||||
| 	if (self->on_connected) | ||||
| 		self->on_connected (self->user_data); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_destroy_connector (struct mpd_client *self) | ||||
| { | ||||
| 	if (self->connector) | ||||
| 		connector_free (self->connector); | ||||
| 	free (self->connector); | ||||
| 	self->connector = NULL; | ||||
| 
 | ||||
| 	// Not connecting anymore
 | ||||
| 	self->state = MPD_DISCONNECTED; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_on_connector_failure (void *user_data) | ||||
| { | ||||
| 	struct mpd_client *self = user_data; | ||||
| 	mpd_client_destroy_connector (self); | ||||
| 	mpd_client_fail (self); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_client_on_connector_connected | ||||
| 	(void *user_data, int socket, const char *host) | ||||
| { | ||||
| 	(void) host; | ||||
| 
 | ||||
| 	struct mpd_client *self = user_data; | ||||
| 	mpd_client_destroy_connector (self); | ||||
| 	mpd_client_finish_connection (self, socket); | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_connect_unix (struct mpd_client *self, const char *address, | ||||
| 	struct error **e) | ||||
| { | ||||
| 	int fd = socket (AF_UNIX, SOCK_STREAM, 0); | ||||
| 	if (fd == -1) | ||||
| 	{ | ||||
| 		error_set (e, "%s: %s", "socket", strerror (errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	// Expand tilde if needed
 | ||||
| 	char *expanded = resolve_filename (address, xstrdup); | ||||
| 
 | ||||
| 	struct sockaddr_un sun; | ||||
| 	sun.sun_family = AF_UNIX; | ||||
| 	strncpy (sun.sun_path, expanded, sizeof sun.sun_path); | ||||
| 	sun.sun_path[sizeof sun.sun_path - 1] = 0; | ||||
| 
 | ||||
| 	free (expanded); | ||||
| 
 | ||||
| 	if (connect (fd, (struct sockaddr *) &sun, sizeof sun)) | ||||
| 	{ | ||||
| 		error_set (e, "%s: %s", "connect", strerror (errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	mpd_client_finish_connection (self, fd); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| mpd_client_connect (struct mpd_client *self, const char *address, | ||||
| 	const char *service, struct error **e) | ||||
| { | ||||
| 	hard_assert (self->state == MPD_DISCONNECTED); | ||||
| 
 | ||||
| 	// If it looks like a path, assume it's a UNIX socket
 | ||||
| 	if (strchr (address, '/')) | ||||
| 		return mpd_client_connect_unix (self, address, e); | ||||
| 
 | ||||
| 	struct connector *connector = xmalloc (sizeof *connector); | ||||
| 	connector_init (connector, self->poller); | ||||
| 	self->connector = connector; | ||||
| 
 | ||||
| 	connector->user_data    = self; | ||||
| 	connector->on_connected = mpd_client_on_connector_connected; | ||||
| 	connector->on_failure   = mpd_client_on_connector_failure; | ||||
| 
 | ||||
| 	connector_add_target (connector, address, service); | ||||
| 	self->state = MPD_CONNECTING; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| // --- NUT ---------------------------------------------------------------------
 | ||||
| 
 | ||||
| // More or less copied and pasted from the MPD client.  This code doesn't even
 | ||||
| @ -2175,6 +1470,16 @@ mpd_on_failure (void *user_data) | ||||
| 	mpd_queue_reconnect (ctx); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mpd_on_io_hook (void *user_data, bool outgoing, const char *line) | ||||
| { | ||||
| 	(void) user_data; | ||||
| 	if (outgoing) | ||||
| 		print_debug ("MPD << %s", line); | ||||
| 	else | ||||
| 		print_debug ("MPD >> %s", line); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| on_mpd_reconnect (void *user_data) | ||||
| { | ||||
| @ -2185,6 +1490,7 @@ on_mpd_reconnect (void *user_data) | ||||
| 	c->on_failure   = mpd_on_failure; | ||||
| 	c->on_connected = mpd_on_connected; | ||||
| 	c->on_event     = mpd_on_events; | ||||
| 	c->on_io_hook   = mpd_on_io_hook; | ||||
| 
 | ||||
| 	struct error *e = NULL; | ||||
| 	if (!mpd_client_connect (&ctx->mpd_client, | ||||
| @ -2262,7 +1568,7 @@ nut_process_ups (struct app_context *ctx, struct str_vector *ups_list, | ||||
| 
 | ||||
| 	struct str_vector v; | ||||
| 	str_vector_init (&v); | ||||
| 	cstr_split_ignore_empty (status, ' ', &v); | ||||
| 	cstr_split (status, " ", true, &v); | ||||
| 	for (size_t i = 0; i < v.len; i++) | ||||
| 	{ | ||||
| 		const char *status = v.vector[i]; | ||||
|  | ||||
| @ -25,13 +25,6 @@ | ||||
| #define PROGRAM_NAME "fancontrol-ng" | ||||
| #include "liberty/liberty.c" | ||||
| 
 | ||||
| /// Shorthand to set an error and return failure from the function
 | ||||
| #define FAIL(...)                                                              \ | ||||
| 	BLOCK_START                                                                \ | ||||
| 		error_set (e, __VA_ARGS__);                                            \ | ||||
| 		return false;                                                          \ | ||||
| 	BLOCK_END | ||||
| 
 | ||||
| // --- Main program ------------------------------------------------------------
 | ||||
| 
 | ||||
| struct device | ||||
| @ -133,8 +126,7 @@ config_validate_nonnegative (const struct config_item *item, struct error **e) | ||||
| 	if (item->value.integer >= 0) | ||||
| 		return true; | ||||
| 
 | ||||
| 	error_set (e, "must be non-negative"); | ||||
| 	return false; | ||||
| 	return error_set (e, "must be non-negative"); | ||||
| } | ||||
| 
 | ||||
| static struct config_schema g_config_device[] = | ||||
| @ -274,10 +266,12 @@ pwm_update (struct paths *paths, struct config_item *pwm, struct error **e) | ||||
| 	int64_t min_start = get_config_integer (pwm, "min_start"); | ||||
| 	int64_t min_stop  = get_config_integer (pwm, "min_stop"); | ||||
| 
 | ||||
| #define FAIL(...) error_set (e, __VA_ARGS__) | ||||
| 	if (min_temp >= max_temp)  FAIL ("min_temp must be less than max_temp"); | ||||
| 	if (pwm_max > 255)         FAIL ("pwm_max must be at most 255"); | ||||
| 	if (min_stop >= pwm_max)   FAIL ("min_stop must be less than pwm_max"); | ||||
| 	if (min_stop < pwm_min)    FAIL ("min_stop must be at least pwm_min"); | ||||
| #undef FAIL | ||||
| 
 | ||||
| 	// I'm not sure if this strangely complicated computation is justifiable
 | ||||
| 	double where | ||||
| @ -423,29 +417,6 @@ device_create (struct app_context *ctx, const char *path, | ||||
| 
 | ||||
| // --- Configuration -----------------------------------------------------------
 | ||||
| 
 | ||||
| // TODO: consider moving to liberty,
 | ||||
| //   degesch and json-rpc-shell have exactly the same function
 | ||||
| static struct config_item * | ||||
| load_configuration_file (const char *filename, struct error **e) | ||||
| { | ||||
| 	struct config_item *root = NULL; | ||||
| 
 | ||||
| 	struct str data; | ||||
| 	str_init (&data); | ||||
| 	if (!read_file (filename, &data, e)) | ||||
| 		goto end; | ||||
| 
 | ||||
| 	struct error *error = NULL; | ||||
| 	if (!(root = config_item_parse (data.str, data.len, false, &error))) | ||||
| 	{ | ||||
| 		error_set (e, "parse error: %s", error->message); | ||||
| 		error_free (error); | ||||
| 	} | ||||
| end: | ||||
| 	str_free (&data); | ||||
| 	return root; | ||||
| } | ||||
| 
 | ||||
| // There is no room for errors in the configuration, everything must be valid.
 | ||||
| // Thus the reset to defaults on invalid values is effectively disabled here.
 | ||||
| static bool | ||||
| @ -470,7 +441,7 @@ apply_schema (struct config_schema *schema, struct config_item *object, | ||||
| 	{ | ||||
| 		// The standard warning is inappropriate here
 | ||||
| 		error_free (warning); | ||||
| 		FAIL ("invalid item `%s'", schema->name); | ||||
| 		return error_set (e, "invalid item `%s'", schema->name); | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| @ -488,9 +459,9 @@ check_device_configuration (struct config_item *subtree, struct error **e) | ||||
| 	if (!(pwms = config_item_get (subtree, "pwms", e))) | ||||
| 		return false; | ||||
| 	if (pwms->type != CONFIG_ITEM_OBJECT) | ||||
| 		FAIL ("`%s' is not an object", "pwms"); | ||||
| 		return error_set (e, "`%s' is not an object", "pwms"); | ||||
| 	if (!pwms->value.object.len) | ||||
| 		FAIL ("no PWMs defined"); | ||||
| 		return error_set (e, "no PWMs defined"); | ||||
| 
 | ||||
| 	// Check regular fields in all PWM subobjects
 | ||||
| 	struct str_map_iter iter; | ||||
| @ -509,7 +480,10 @@ check_device_configuration (struct config_item *subtree, struct error **e) | ||||
| 				return false; | ||||
| 			} | ||||
| 		if (!get_config_string (pwm, "temp")) | ||||
| 			FAIL ("PWM `%s': %s", subpath, "`temp' cannot be null"); | ||||
| 		{ | ||||
| 			return error_set (e, | ||||
| 				"PWM `%s': %s", subpath, "`temp' cannot be null"); | ||||
| 		} | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| @ -518,7 +492,7 @@ static void | ||||
| load_configuration (struct app_context *ctx, const char *config_path) | ||||
| { | ||||
| 	struct error *e = NULL; | ||||
| 	struct config_item *root = load_configuration_file (config_path, &e); | ||||
| 	struct config_item *root = config_read_from_file (config_path, &e); | ||||
| 
 | ||||
| 	if (e) | ||||
| 	{ | ||||
|  | ||||
							
								
								
									
										2
									
								
								liberty
									
									
									
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								liberty
									
									
									
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit 052d2ffc9a3141ef2bb771f70190ed7a0bb9da44 | ||||
| Subproject commit 2a15b1de700eb4e20c6bebb9742c8e20fffc9687 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user