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 #include "config.h" 25 26 /* size of the dbus object path length */ 27 const size_t dbus_obj_path_len = 1024; 28 29 #define DBUS_ERR "org.openbmc.error" 30 #define DBUS_NAME "xyz.openbmc_project.Console.%s" 31 #define OBJ_NAME "/xyz/openbmc_project/console/%s" 32 #define UART_INTF "xyz.openbmc_project.Console.UART" 33 #define ACCESS_INTF "xyz.openbmc_project.Console.Access" 34 35 static void tty_change_baudrate(struct console *console) 36 { 37 int i; 38 int rc; 39 40 tty_init_termios(console->server); 41 42 for (i = 0; i < console->n_handlers; i++) { 43 const struct handler_type *type; 44 struct handler *handler; 45 46 handler = console->handlers[i]; 47 type = handler->type; 48 if (!type->baudrate) { 49 continue; 50 } 51 52 rc = type->baudrate(handler, console->server->tty.uart.baud); 53 if (rc) { 54 warnx("Can't set terminal baudrate for handler %s", 55 type->name); 56 } 57 } 58 } 59 60 static int set_baud_handler(sd_bus *bus, const char *path, 61 const char *interface, const char *property, 62 sd_bus_message *msg, void *userdata, 63 sd_bus_error *err __attribute__((unused))) 64 { 65 struct console *console = userdata; 66 uint64_t baudrate; 67 speed_t speed; 68 int r; 69 70 if (!console) { 71 return -ENOENT; 72 } 73 74 r = sd_bus_message_read(msg, "t", &baudrate); 75 if (r < 0 || baudrate > UINT32_MAX) { 76 return -EINVAL; 77 } 78 79 speed = parse_int_to_baud((uint32_t)baudrate); 80 if (!speed) { 81 warnx("Invalid baud rate: '%" PRIu64 "'", baudrate); 82 return -EINVAL; 83 } 84 85 assert(console->server->tty.type == TTY_DEVICE_UART); 86 console->server->tty.uart.baud = speed; 87 tty_change_baudrate(console); 88 89 sd_bus_emit_properties_changed(bus, path, interface, property, NULL); 90 91 return 1; 92 } 93 94 static int get_baud_handler(sd_bus *bus __attribute__((unused)), 95 const char *path __attribute__((unused)), 96 const char *interface __attribute__((unused)), 97 const char *property __attribute__((unused)), 98 sd_bus_message *reply, void *userdata, 99 sd_bus_error *error __attribute__((unused))) 100 { 101 struct console *console = userdata; 102 struct console_server *server = console->server; 103 uint64_t baudrate; 104 int r; 105 106 assert(server->tty.type == TTY_DEVICE_UART); 107 baudrate = parse_baud_to_int(server->tty.uart.baud); 108 if (!baudrate) { 109 warnx("Invalid baud rate: '%d'", server->tty.uart.baud); 110 } 111 112 r = sd_bus_message_append(reply, "t", baudrate); 113 114 return r; 115 } 116 117 static int method_connect(sd_bus_message *msg, void *userdata, 118 sd_bus_error *err) 119 { 120 struct console *console = userdata; 121 int rc; 122 int socket_fd = -1; 123 124 if (!console) { 125 warnx("Internal error: Console pointer is null"); 126 sd_bus_error_set_const(err, DBUS_ERR, "Internal error"); 127 return sd_bus_reply_method_error(msg, err); 128 } 129 130 /* Register the consumer. */ 131 socket_fd = dbus_create_socket_consumer(console); 132 if (socket_fd < 0) { 133 rc = socket_fd; 134 warnx("Failed to create socket consumer: %s", strerror(rc)); 135 sd_bus_error_set_const(err, DBUS_ERR, 136 "Failed to create socket consumer"); 137 return sd_bus_reply_method_error(msg, err); 138 } 139 140 rc = sd_bus_reply_method_return(msg, "h", socket_fd); 141 142 /* Close the our end */ 143 close(socket_fd); 144 145 return rc; 146 } 147 148 static const sd_bus_vtable console_uart_vtable[] = { 149 SD_BUS_VTABLE_START(0), 150 SD_BUS_WRITABLE_PROPERTY("Baud", "t", get_baud_handler, 151 set_baud_handler, 0, 152 SD_BUS_VTABLE_UNPRIVILEGED | 153 SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), 154 SD_BUS_VTABLE_END, 155 }; 156 157 static const sd_bus_vtable console_access_vtable[] = { 158 SD_BUS_VTABLE_START(0), 159 SD_BUS_METHOD("Connect", SD_BUS_NO_ARGS, "h", method_connect, 160 SD_BUS_VTABLE_UNPRIVILEGED), 161 SD_BUS_VTABLE_END, 162 }; 163 164 int dbus_init(struct console *console, 165 struct config *config __attribute__((unused))) 166 { 167 char obj_name[dbus_obj_path_len]; 168 char dbus_name[dbus_obj_path_len]; 169 int fd; 170 int r; 171 size_t bytes; 172 173 if (!console) { 174 warnx("Couldn't get valid console"); 175 return -1; 176 } 177 178 r = sd_bus_default(&console->bus); 179 if (r < 0) { 180 warnx("Failed to connect to bus: %s", strerror(-r)); 181 return -1; 182 } 183 184 /* Register support console interface */ 185 bytes = snprintf(obj_name, dbus_obj_path_len, OBJ_NAME, 186 console->console_id); 187 if (bytes >= dbus_obj_path_len) { 188 warnx("Console id '%s' is too long. There is no enough space in the buffer.", 189 console->console_id); 190 return -1; 191 } 192 193 if (console->server->tty.type == TTY_DEVICE_UART) { 194 /* Register UART interface */ 195 r = sd_bus_add_object_vtable(console->bus, NULL, obj_name, 196 UART_INTF, console_uart_vtable, 197 console); 198 if (r < 0) { 199 warnx("Failed to register UART interface: %s", 200 strerror(-r)); 201 return -1; 202 } 203 } 204 205 /* Register access interface */ 206 r = sd_bus_add_object_vtable(console->bus, NULL, obj_name, ACCESS_INTF, 207 console_access_vtable, console); 208 if (r < 0) { 209 warnx("Failed to issue method call: %s", strerror(-r)); 210 return -1; 211 } 212 213 bytes = snprintf(dbus_name, dbus_obj_path_len, DBUS_NAME, 214 console->console_id); 215 if (bytes >= dbus_obj_path_len) { 216 warnx("Console id '%s' is too long. There is no enough space in the buffer.", 217 console->console_id); 218 return -1; 219 } 220 221 /* Finally register the bus name */ 222 r = sd_bus_request_name(console->bus, dbus_name, 223 SD_BUS_NAME_ALLOW_REPLACEMENT | 224 SD_BUS_NAME_REPLACE_EXISTING); 225 if (r < 0) { 226 warnx("Failed to acquire service name: %s", strerror(-r)); 227 return -1; 228 } 229 230 fd = sd_bus_get_fd(console->bus); 231 if (fd < 0) { 232 warnx("Couldn't get the bus file descriptor"); 233 return -1; 234 } 235 236 const ssize_t index = 237 console_server_request_pollfd(console->server, fd, POLLIN); 238 if (index < 0) { 239 fprintf(stderr, "Error: failed to allocate pollfd\n"); 240 return -1; 241 } 242 243 console->dbus_pollfd_index = index; 244 return 0; 245 } 246