1 /** 2 * Copyright © 2023 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <assert.h> 18 #include <errno.h> 19 #include <err.h> 20 #include <string.h> 21 #include <sys/socket.h> 22 23 #include "console-server.h" 24 25 /* size of the dbus object path length */ 26 const size_t dbus_obj_path_len = 1024; 27 28 #define DBUS_ERR "org.openbmc.error" 29 #define DBUS_NAME "xyz.openbmc_project.Console.%s" 30 #define OBJ_NAME "/xyz/openbmc_project/console/%s" 31 #define UART_INTF "xyz.openbmc_project.Console.UART" 32 #define ACCESS_INTF "xyz.openbmc_project.Console.Access" 33 34 static void tty_change_baudrate(struct console *console) 35 { 36 struct handler *handler; 37 int i; 38 int rc; 39 40 tty_init_termios(console); 41 42 for (i = 0; i < console->n_handlers; i++) { 43 handler = console->handlers[i]; 44 if (!handler->baudrate) { 45 continue; 46 } 47 48 rc = handler->baudrate(handler, console->tty.uart.baud); 49 if (rc) { 50 warnx("Can't set terminal baudrate for handler %s", 51 handler->name); 52 } 53 } 54 } 55 56 static int set_baud_handler(sd_bus *bus, const char *path, 57 const char *interface, const char *property, 58 sd_bus_message *msg, void *userdata, 59 sd_bus_error *err __attribute__((unused))) 60 { 61 struct console *console = userdata; 62 uint64_t baudrate; 63 speed_t speed; 64 int r; 65 66 if (!console) { 67 return -ENOENT; 68 } 69 70 r = sd_bus_message_read(msg, "t", &baudrate); 71 if (r < 0 || baudrate > UINT32_MAX) { 72 return -EINVAL; 73 } 74 75 speed = parse_int_to_baud((uint32_t)baudrate); 76 if (!speed) { 77 warnx("Invalid baud rate: '%" PRIu64 "'", baudrate); 78 return -EINVAL; 79 } 80 81 assert(console->tty.type == TTY_DEVICE_UART); 82 console->tty.uart.baud = speed; 83 tty_change_baudrate(console); 84 85 sd_bus_emit_properties_changed(bus, path, interface, property, NULL); 86 87 return 1; 88 } 89 90 static int get_baud_handler(sd_bus *bus __attribute__((unused)), 91 const char *path __attribute__((unused)), 92 const char *interface __attribute__((unused)), 93 const char *property __attribute__((unused)), 94 sd_bus_message *reply, void *userdata, 95 sd_bus_error *error __attribute__((unused))) 96 { 97 struct console *console = userdata; 98 uint64_t baudrate; 99 int r; 100 101 assert(console->tty.type == TTY_DEVICE_UART); 102 baudrate = parse_baud_to_int(console->tty.uart.baud); 103 if (!baudrate) { 104 warnx("Invalid baud rate: '%d'", console->tty.uart.baud); 105 } 106 107 r = sd_bus_message_append(reply, "t", baudrate); 108 109 return r; 110 } 111 112 static int method_connect(sd_bus_message *msg, void *userdata, 113 sd_bus_error *err) 114 { 115 struct console *console = userdata; 116 int rc; 117 int socket_fd = -1; 118 119 if (!console) { 120 warnx("Internal error: Console pointer is null"); 121 sd_bus_error_set_const(err, DBUS_ERR, "Internal error"); 122 return sd_bus_reply_method_error(msg, err); 123 } 124 125 /* Register the consumer. */ 126 socket_fd = dbus_create_socket_consumer(console); 127 if (socket_fd < 0) { 128 rc = socket_fd; 129 warnx("Failed to create socket consumer: %s", strerror(rc)); 130 sd_bus_error_set_const(err, DBUS_ERR, 131 "Failed to create socket consumer"); 132 return sd_bus_reply_method_error(msg, err); 133 } 134 135 rc = sd_bus_reply_method_return(msg, "h", socket_fd); 136 137 /* Close the our end */ 138 close(socket_fd); 139 140 return rc; 141 } 142 143 static const sd_bus_vtable console_uart_vtable[] = { 144 SD_BUS_VTABLE_START(0), 145 SD_BUS_WRITABLE_PROPERTY("Baud", "t", get_baud_handler, 146 set_baud_handler, 0, 147 SD_BUS_VTABLE_UNPRIVILEGED | 148 SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), 149 SD_BUS_VTABLE_END, 150 }; 151 152 static const sd_bus_vtable console_access_vtable[] = { 153 SD_BUS_VTABLE_START(0), 154 SD_BUS_METHOD("Connect", SD_BUS_NO_ARGS, "h", method_connect, 155 SD_BUS_VTABLE_UNPRIVILEGED), 156 SD_BUS_VTABLE_END, 157 }; 158 159 void dbus_init(struct console *console, 160 struct config *config __attribute__((unused))) 161 { 162 char obj_name[dbus_obj_path_len]; 163 char dbus_name[dbus_obj_path_len]; 164 int dbus_poller = 0; 165 int fd; 166 int r; 167 size_t bytes; 168 169 if (!console) { 170 warnx("Couldn't get valid console"); 171 return; 172 } 173 174 r = sd_bus_default_system(&console->bus); 175 if (r < 0) { 176 warnx("Failed to connect to system bus: %s", strerror(-r)); 177 return; 178 } 179 180 /* Register support console interface */ 181 bytes = snprintf(obj_name, dbus_obj_path_len, OBJ_NAME, 182 console->console_id); 183 if (bytes >= dbus_obj_path_len) { 184 warnx("Console id '%s' is too long. There is no enough space in the buffer.", 185 console->console_id); 186 return; 187 } 188 189 if (console->tty.type == TTY_DEVICE_UART) { 190 /* Register UART interface */ 191 r = sd_bus_add_object_vtable(console->bus, NULL, obj_name, 192 UART_INTF, console_uart_vtable, 193 console); 194 if (r < 0) { 195 warnx("Failed to register UART interface: %s", 196 strerror(-r)); 197 return; 198 } 199 } 200 201 /* Register access interface */ 202 r = sd_bus_add_object_vtable(console->bus, NULL, obj_name, ACCESS_INTF, 203 console_access_vtable, console); 204 if (r < 0) { 205 warnx("Failed to issue method call: %s", strerror(-r)); 206 return; 207 } 208 209 bytes = snprintf(dbus_name, dbus_obj_path_len, DBUS_NAME, 210 console->console_id); 211 if (bytes >= dbus_obj_path_len) { 212 warnx("Console id '%s' is too long. There is no enough space in the buffer.", 213 console->console_id); 214 return; 215 } 216 217 /* Finally register the bus name */ 218 r = sd_bus_request_name(console->bus, dbus_name, 219 SD_BUS_NAME_ALLOW_REPLACEMENT | 220 SD_BUS_NAME_REPLACE_EXISTING); 221 if (r < 0) { 222 warnx("Failed to acquire service name: %s", strerror(-r)); 223 return; 224 } 225 226 fd = sd_bus_get_fd(console->bus); 227 if (fd < 0) { 228 warnx("Couldn't get the bus file descriptor"); 229 return; 230 } 231 232 dbus_poller = POLLFD_DBUS; 233 234 console->pollfds[dbus_poller].fd = fd; 235 console->pollfds[dbus_poller].events = POLLIN; 236 } 237