@ -47,13 +47,14 @@ struct ipc_client {
struct wl_event_source * writable_event_source ;
struct wl_event_source * writable_event_source ;
struct sway_server * server ;
struct sway_server * server ;
int fd ;
int fd ;
uint32_t payload_length ;
uint32_t security_policy ;
uint32_t security_policy ;
enum ipc_command_type current_command ;
enum ipc_command_type subscribed_events ;
enum ipc_command_type subscribed_events ;
size_t write_buffer_len ;
size_t write_buffer_len ;
size_t write_buffer_size ;
size_t write_buffer_size ;
char * write_buffer ;
char * write_buffer ;
// The following are for storing data between event_loop calls
uint32_t pending_length ;
enum ipc_command_type pending_type ;
} ;
} ;
struct sockaddr_un * ipc_user_sockaddr ( void ) ;
struct sockaddr_un * ipc_user_sockaddr ( void ) ;
@ -61,8 +62,10 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data);
int ipc_client_handle_readable ( int client_fd , uint32_t mask , void * data ) ;
int ipc_client_handle_readable ( int client_fd , uint32_t mask , void * data ) ;
int ipc_client_handle_writable ( int client_fd , uint32_t mask , void * data ) ;
int ipc_client_handle_writable ( int client_fd , uint32_t mask , void * data ) ;
void ipc_client_disconnect ( struct ipc_client * client ) ;
void ipc_client_disconnect ( struct ipc_client * client ) ;
void ipc_client_handle_command ( struct ipc_client * client ) ;
void ipc_client_handle_command ( struct ipc_client * client , uint32_t payload_length ,
bool ipc_send_reply ( struct ipc_client * client , const char * payload , uint32_t payload_length ) ;
enum ipc_command_type payload_type ) ;
bool ipc_send_reply ( struct ipc_client * client , enum ipc_command_type payload_type ,
const char * payload , uint32_t payload_length ) ;
static void handle_display_destroy ( struct wl_listener * listener , void * data ) {
static void handle_display_destroy ( struct wl_listener * listener , void * data ) {
if ( ipc_event_source ) {
if ( ipc_event_source ) {
@ -178,7 +181,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
return 0 ;
return 0 ;
}
}
client - > server = server ;
client - > server = server ;
client - > p ayload _length = 0 ;
client - > p ending _length = 0 ;
client - > fd = client_fd ;
client - > fd = client_fd ;
client - > subscribed_events = 0 ;
client - > subscribed_events = 0 ;
client - > event_source = wl_event_loop_add_fd ( server - > wl_event_loop ,
client - > event_source = wl_event_loop_add_fd ( server - > wl_event_loop ,
@ -224,9 +227,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
}
}
// Wait for the rest of the command payload in case the header has already been read
// Wait for the rest of the command payload in case the header has already been read
if ( client - > payload_length > 0 ) {
if ( client - > pending_length > 0 ) {
if ( ( uint32_t ) read_available > = client - > payload_length ) {
if ( ( uint32_t ) read_available > = client - > pending_length ) {
ipc_client_handle_command ( client ) ;
// Reset pending values.
uint32_t pending_length = client - > pending_length ;
enum ipc_command_type pending_type = client - > pending_type ;
client - > pending_length = 0 ;
ipc_client_handle_command ( client , pending_length , pending_type ) ;
}
}
return 0 ;
return 0 ;
}
}
@ -251,11 +258,15 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
return 0 ;
return 0 ;
}
}
memcpy ( & client - > p ayload _length, & buf32 [ 0 ] , sizeof ( buf32 [ 0 ] ) ) ;
memcpy ( & client - > p ending _length, & buf32 [ 0 ] , sizeof ( buf32 [ 0 ] ) ) ;
memcpy ( & client - > current_command , & buf32 [ 1 ] , sizeof ( buf32 [ 1 ] ) ) ;
memcpy ( & client - > pending_type , & buf32 [ 1 ] , sizeof ( buf32 [ 1 ] ) ) ;
if ( read_available - received > = ( long ) client - > payload_length ) {
if ( read_available - received > = ( long ) client - > pending_length ) {
ipc_client_handle_command ( client ) ;
// Reset pending values.
uint32_t pending_length = client - > pending_length ;
enum ipc_command_type pending_type = client - > pending_type ;
client - > pending_length = 0 ;
ipc_client_handle_command ( client , pending_length , pending_type ) ;
}
}
return 0 ;
return 0 ;
@ -278,8 +289,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)
if ( ( client - > subscribed_events & event_mask ( event ) ) = = 0 ) {
if ( ( client - > subscribed_events & event_mask ( event ) ) = = 0 ) {
continue ;
continue ;
}
}
client - > current_command = event ;
if ( ! ipc_send_reply ( client , event , json_string ,
if ( ! ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ) {
( uint32_t ) strlen ( json_string ) ) ) {
sway_log_errno ( SWAY_INFO , " Unable to send reply to IPC client " ) ;
sway_log_errno ( SWAY_INFO , " Unable to send reply to IPC client " ) ;
/* ipc_send_reply destroys client on error, which also
/* ipc_send_reply destroys client on error, which also
* removes it from the list , so we need to process
* removes it from the list , so we need to process
@ -567,20 +578,21 @@ static void ipc_get_marks_callback(struct sway_container *con, void *data) {
}
}
}
}
void ipc_client_handle_command ( struct ipc_client * client ) {
void ipc_client_handle_command ( struct ipc_client * client , uint32_t payload_length ,
enum ipc_command_type payload_type ) {
if ( ! sway_assert ( client ! = NULL , " client != NULL " ) ) {
if ( ! sway_assert ( client ! = NULL , " client != NULL " ) ) {
return ;
return ;
}
}
char * buf = malloc ( client- > payload_length + 1 ) ;
char * buf = malloc ( payload_length + 1 ) ;
if ( ! buf ) {
if ( ! buf ) {
sway_log_errno ( SWAY_INFO , " Unable to allocate IPC payload " ) ;
sway_log_errno ( SWAY_INFO , " Unable to allocate IPC payload " ) ;
ipc_client_disconnect ( client ) ;
ipc_client_disconnect ( client ) ;
return ;
return ;
}
}
if ( client- > payload_length > 0 ) {
if ( payload_length > 0 ) {
// Payload should be fully available
// Payload should be fully available
ssize_t received = recv ( client - > fd , buf , client- > payload_length, 0 ) ;
ssize_t received = recv ( client - > fd , buf , payload_length, 0 ) ;
if ( received = = - 1 )
if ( received = = - 1 )
{
{
sway_log_errno ( SWAY_INFO , " Unable to receive payload from IPC client " ) ;
sway_log_errno ( SWAY_INFO , " Unable to receive payload from IPC client " ) ;
@ -589,16 +601,15 @@ void ipc_client_handle_command(struct ipc_client *client) {
return ;
return ;
}
}
}
}
buf [ client- > payload_length] = ' \0 ' ;
buf [ payload_length] = ' \0 ' ;
bool client_valid = true ;
switch ( payload_type ) {
switch ( client - > current_command ) {
case IPC_COMMAND :
case IPC_COMMAND :
{
{
char * line = strtok ( buf , " \n " ) ;
char * line = strtok ( buf , " \n " ) ;
while ( line ) {
while ( line ) {
size_t line_length = strlen ( line ) ;
size_t line_length = strlen ( line ) ;
if ( line + line_length > = buf + client- > payload_length) {
if ( line + line_length > = buf + payload_length) {
break ;
break ;
}
}
line [ line_length ] = ' ; ' ;
line [ line_length ] = ' ; ' ;
@ -609,7 +620,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
transaction_commit_dirty ( ) ;
transaction_commit_dirty ( ) ;
char * json = cmd_results_to_json ( res_list ) ;
char * json = cmd_results_to_json ( res_list ) ;
int length = strlen ( json ) ;
int length = strlen ( json ) ;
client_valid = ipc_send_reply ( client , json , ( uint32_t ) length ) ;
ipc_send_reply ( client , payload_type , json , ( uint32_t ) length ) ;
free ( json ) ;
free ( json ) ;
while ( res_list - > length ) {
while ( res_list - > length ) {
struct cmd_results * results = res_list - > items [ 0 ] ;
struct cmd_results * results = res_list - > items [ 0 ] ;
@ -623,7 +634,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
case IPC_SEND_TICK :
case IPC_SEND_TICK :
{
{
ipc_event_tick ( buf ) ;
ipc_event_tick ( buf ) ;
ipc_send_reply ( client , " { \" success \" : true} " , 17 ) ;
ipc_send_reply ( client , payload_type , " { \" success \" : true} " , 17 ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -656,8 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
}
}
}
}
const char * json_string = json_object_to_json_string ( outputs ) ;
const char * json_string = json_object_to_json_string ( outputs ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( outputs ) ; // free
json_object_put ( outputs ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -667,8 +678,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object * workspaces = json_object_new_array ( ) ;
json_object * workspaces = json_object_new_array ( ) ;
root_for_each_workspace ( ipc_get_workspaces_callback , workspaces ) ;
root_for_each_workspace ( ipc_get_workspaces_callback , workspaces ) ;
const char * json_string = json_object_to_json_string ( workspaces ) ;
const char * json_string = json_object_to_json_string ( workspaces ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( workspaces ) ; // free
json_object_put ( workspaces ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -679,7 +690,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
struct json_object * request = json_tokener_parse ( buf ) ;
struct json_object * request = json_tokener_parse ( buf ) ;
if ( request = = NULL | | ! json_object_is_type ( request , json_type_array ) ) {
if ( request = = NULL | | ! json_object_is_type ( request , json_type_array ) ) {
const char msg [ ] = " { \" success \" : false} " ;
const char msg [ ] = " { \" success \" : false} " ;
client_valid = ipc_send_reply ( client , msg , strlen ( msg ) ) ;
ipc_send_reply ( client , payload_type , msg , strlen ( msg ) ) ;
sway_log ( SWAY_INFO , " Failed to parse subscribe request " ) ;
sway_log ( SWAY_INFO , " Failed to parse subscribe request " ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -707,7 +718,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
is_tick = true ;
is_tick = true ;
} else {
} else {
const char msg [ ] = " { \" success \" : false} " ;
const char msg [ ] = " { \" success \" : false} " ;
client_valid = ipc_send_reply ( client , msg , strlen ( msg ) ) ;
ipc_send_reply ( client , payload_type , msg , strlen ( msg ) ) ;
json_object_put ( request ) ;
json_object_put ( request ) ;
sway_log ( SWAY_INFO , " Unsupported event type in subscribe request " ) ;
sway_log ( SWAY_INFO , " Unsupported event type in subscribe request " ) ;
goto exit_cleanup ;
goto exit_cleanup ;
@ -716,11 +727,11 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_put ( request ) ;
json_object_put ( request ) ;
const char msg [ ] = " { \" success \" : true} " ;
const char msg [ ] = " { \" success \" : true} " ;
client_valid = ipc_send_reply ( client , msg , strlen ( msg ) ) ;
ipc_send_reply ( client , payload_type , msg , strlen ( msg ) ) ;
if ( is_tick ) {
if ( is_tick ) {
client - > current_command = IPC_EVENT_TICK ;
const char tickmsg [ ] = " { \" first \" : true, \" payload \" : \" \" } " ;
const char tickmsg [ ] = " { \" first \" : true, \" payload \" : \" \" } " ;
ipc_send_reply ( client , tickmsg , strlen ( tickmsg ) ) ;
ipc_send_reply ( client , IPC_EVENT_TICK , tickmsg ,
strlen ( tickmsg ) ) ;
}
}
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -733,8 +744,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_array_add ( inputs , ipc_json_describe_input ( device ) ) ;
json_object_array_add ( inputs , ipc_json_describe_input ( device ) ) ;
}
}
const char * json_string = json_object_to_json_string ( inputs ) ;
const char * json_string = json_object_to_json_string ( inputs ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( inputs ) ; // free
json_object_put ( inputs ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -747,8 +758,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_array_add ( seats , ipc_json_describe_seat ( seat ) ) ;
json_object_array_add ( seats , ipc_json_describe_seat ( seat ) ) ;
}
}
const char * json_string = json_object_to_json_string ( seats ) ;
const char * json_string = json_object_to_json_string ( seats ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( seats ) ; // free
json_object_put ( seats ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -757,8 +768,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
{
{
json_object * tree = ipc_json_describe_node_recursive ( & root - > node ) ;
json_object * tree = ipc_json_describe_node_recursive ( & root - > node ) ;
const char * json_string = json_object_to_json_string ( tree ) ;
const char * json_string = json_object_to_json_string ( tree ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( tree ) ;
json_object_put ( tree ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -768,8 +779,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object * marks = json_object_new_array ( ) ;
json_object * marks = json_object_new_array ( ) ;
root_for_each_container ( ipc_get_marks_callback , marks ) ;
root_for_each_container ( ipc_get_marks_callback , marks ) ;
const char * json_string = json_object_to_json_string ( marks ) ;
const char * json_string = json_object_to_json_string ( marks ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( marks ) ;
json_object_put ( marks ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -778,8 +789,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
{
{
json_object * version = ipc_json_get_version ( ) ;
json_object * version = ipc_json_get_version ( ) ;
const char * json_string = json_object_to_json_string ( version ) ;
const char * json_string = json_object_to_json_string ( version ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( version ) ; // free
json_object_put ( version ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -794,8 +805,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_array_add ( bars , json_object_new_string ( bar - > id ) ) ;
json_object_array_add ( bars , json_object_new_string ( bar - > id ) ) ;
}
}
const char * json_string = json_object_to_json_string ( bars ) ;
const char * json_string = json_object_to_json_string ( bars ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string ,
( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( bars ) ; // free
json_object_put ( bars ) ; // free
} else {
} else {
@ -810,14 +820,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
}
}
if ( ! bar ) {
if ( ! bar ) {
const char * error = " { \" success \" : false, \" error \" : \" No bar with that ID \" } " ;
const char * error = " { \" success \" : false, \" error \" : \" No bar with that ID \" } " ;
client_valid =
ipc_send_reply ( client , payload_type , error ,
ipc_send_reply ( client , error , ( uint32_t ) strlen ( error ) ) ;
( uint32_t ) strlen ( error ) ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
json_object * json = ipc_json_describe_bar_config ( bar ) ;
json_object * json = ipc_json_describe_bar_config ( bar ) ;
const char * json_string = json_object_to_json_string ( json ) ;
const char * json_string = json_object_to_json_string ( json ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string ,
( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( json ) ; // free
json_object_put ( json ) ; // free
}
}
@ -832,8 +841,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_array_add ( modes , json_object_new_string ( mode - > name ) ) ;
json_object_array_add ( modes , json_object_new_string ( mode - > name ) ) ;
}
}
const char * json_string = json_object_to_json_string ( modes ) ;
const char * json_string = json_object_to_json_string ( modes ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( modes ) ; // free
json_object_put ( modes ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -843,8 +852,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object * json = json_object_new_object ( ) ;
json_object * json = json_object_new_object ( ) ;
json_object_object_add ( json , " config " , json_object_new_string ( config - > current_config ) ) ;
json_object_object_add ( json , " config " , json_object_new_string ( config - > current_config ) ) ;
const char * json_string = json_object_to_json_string ( json ) ;
const char * json_string = json_object_to_json_string ( json ) ;
client_valid =
ipc_send_reply ( client , payload_type , json_string ,
ipc_send_reply ( client , json_string , ( uint32_t ) strlen ( json_string ) ) ;
( uint32_t ) strlen ( json_string ) ) ;
json_object_put ( json ) ; // free
json_object_put ( json ) ; // free
goto exit_cleanup ;
goto exit_cleanup ;
}
}
@ -853,24 +862,22 @@ void ipc_client_handle_command(struct ipc_client *client) {
{
{
// It was decided sway will not support this, just return success:false
// It was decided sway will not support this, just return success:false
const char msg [ ] = " { \" success \" : false} " ;
const char msg [ ] = " { \" success \" : false} " ;
ipc_send_reply ( client , msg, strlen ( msg ) ) ;
ipc_send_reply ( client , payload_type, msg, strlen ( msg ) ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
default :
default :
sway_log ( SWAY_INFO , " Unknown IPC command type % i" , client - > current_command ) ;
sway_log ( SWAY_INFO , " Unknown IPC command type % x" , payload_type ) ;
goto exit_cleanup ;
goto exit_cleanup ;
}
}
exit_cleanup :
exit_cleanup :
if ( client_valid ) {
client - > payload_length = 0 ;
}
free ( buf ) ;
free ( buf ) ;
return ;
return ;
}
}
bool ipc_send_reply ( struct ipc_client * client , const char * payload , uint32_t payload_length ) {
bool ipc_send_reply ( struct ipc_client * client , enum ipc_command_type payload_type ,
const char * payload , uint32_t payload_length ) {
assert ( payload ) ;
assert ( payload ) ;
char data [ IPC_HEADER_SIZE ] ;
char data [ IPC_HEADER_SIZE ] ;
@ -878,7 +885,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
memcpy ( data , ipc_magic , sizeof ( ipc_magic ) ) ;
memcpy ( data , ipc_magic , sizeof ( ipc_magic ) ) ;
memcpy ( & data32 [ 0 ] , & payload_length , sizeof ( payload_length ) ) ;
memcpy ( & data32 [ 0 ] , & payload_length , sizeof ( payload_length ) ) ;
memcpy ( & data32 [ 1 ] , & client- > current_command , sizeof ( client - > current_command ) ) ;
memcpy ( & data32 [ 1 ] , & payload_type, sizeof ( payload_type ) ) ;
while ( client - > write_buffer_len + IPC_HEADER_SIZE + payload_length > =
while ( client - > write_buffer_len + IPC_HEADER_SIZE + payload_length > =
client - > write_buffer_size ) {
client - > write_buffer_size ) {
@ -910,6 +917,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
ipc_client_handle_writable , client ) ;
ipc_client_handle_writable , client ) ;
}
}
sway_log ( SWAY_DEBUG , " Added IPC reply to client %d queue: %s " , client - > fd , payload ) ;
sway_log ( SWAY_DEBUG , " Added IPC reply of type 0x%x to client %d queue: %s " ,
payload_type , client - > fd , payload ) ;
return true ;
return true ;
}
}