1 /*
2 * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com/
3 *
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * Neither the name of Texas Instruments Incorporated nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
34 /* Standard includes */
35 #include <stdio.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 #include <signal.h>
42 #include <fcntl.h>
43 #include <getopt.h>
44 #include <unistd.h>
46 #include <libdaemon/daemon.h>
48 /* Socket Includes */
49 #include "serverlogutil.h"
50 #include "sockutils.h"
51 #include "sockrmmsg.h"
53 /* RM includes */
54 #include <ti/drv/rm/rm.h>
55 #include <ti/drv/rm/rm_transport.h>
57 #define RMSERVER_DAEMON_PID_FILE_NAME "/var/run/rmServer/pid"
59 /* Error checking macro */
60 #define ERROR_CHECK(checkVal, resultVal, rmInstName, printMsg) \
61 if (resultVal != checkVal) { \
62 char errorMsgToPrint[] = printMsg; \
63 error_msg("%s : ", rmInstName); \
64 error_msg("%s with error code : %d\n", errorMsgToPrint, resultVal); \
65 exit(EXIT_FAILURE); \
66 }
68 /* logging errors */
69 #define error_msg(...) rmsrv_log(LOG_ERR, __func__, __FILE__, __LINE__, __VA_ARGS__);
70 /* logging warnings */
71 #define warning_msg(...) rmsrv_log(LOG_WARNING, __func__, __FILE__, __LINE__, __VA_ARGS__);
72 /* logging information */
73 #define info_msg(...) rmsrv_log(LOG_INFO, __func__, __FILE__, __LINE__, __VA_ARGS__);
74 /* logging debug information */
75 #define debug_msg(...) rmsrv_log(LOG_DEBUG, __func__, __FILE__, __LINE__, __VA_ARGS__);
77 #define LOG_APPEND(x) \
78 do { \
79 int len; \
80 len = MAX_PRE_LOG_BUF_LEN - strlen(rmsrv_log_buf); \
81 if (len > 0) { \
82 strncat(rmsrv_log_buf, x, len); \
83 } \
84 else { \
85 return; \
86 } \
87 } while(0)
89 /* RM registered transport mapping structure */
90 typedef struct trans_map_entry_s {
91 /* Registered RM transport handle */
92 Rm_TransportHandle trans_handle;
93 /* Remote socket tied to the transport handle */
94 sock_name_t *remote_sock;
95 /* Next entry in the transport map */
96 struct trans_map_entry_s *n;
97 } trans_map_entry_t;
99 /**********************************************************************
100 ********************** Global Variables ******************************
101 **********************************************************************/
103 /* RM Server instance name (must match with RM Global Resource List (GRL) and policies */
104 char server_name[RM_NAME_MAX_CHARS] = "RM_Server";
106 Rm_Handle server_h;
108 sock_h server_sock = NULL;
110 rmserver_cfg_t rmsrv_cfg;
112 char rmsrv_log_buf[MAX_PRE_LOG_BUF_LEN];
114 /**********************************************************************
115 ********************** External Variables ****************************
116 **********************************************************************/
118 extern int optind;
119 extern char *optarg;
121 /**********************************************************************
122 ************************** Server Functions **************************
123 **********************************************************************/
125 /* RM Server logging utility (need to move it to another file) */
126 void rmsrv_log(const int loglevel, const char* functionName, const char* fileName, const int lineNo, const char* format, ...)
127 {
128 int len;
129 char lineno_a[32];
130 va_list args;
131 struct stat fbuf;
133 sprintf(lineno_a, "%d", lineNo);
135 rmsrv_log_buf[0] = 0;
137 LOG_APPEND(fileName);
138 LOG_APPEND(":");
139 LOG_APPEND(lineno_a);
140 LOG_APPEND(":");
141 LOG_APPEND(functionName);
142 LOG_APPEND(":");
144 len = MAX_PRE_LOG_BUF_LEN - strlen(rmsrv_log_buf);
145 if (len <= 0) {
146 return;
147 }
149 va_start(args, format);
150 vsnprintf(&rmsrv_log_buf[strlen(rmsrv_log_buf)], len, format, args);
151 va_end(args);
153 /* logfile reset if going over max_len */
154 fstat(fileno(rmsrv_cfg.logfile_p), &fbuf);
155 if ((fbuf.st_size + strlen(rmsrv_log_buf)) > rmsrv_cfg.logfile_max_len) {
156 freopen(RMSERVER_DAEMON_LOG_FILE_NAME, "w+", rmsrv_cfg.logfile_p);
157 }
159 fprintf(rmsrv_cfg.logfile_p, "%s", rmsrv_log_buf);
160 fflush(rmsrv_cfg.logfile_p);
161 }
163 Rm_Packet *transportAlloc(Rm_AppTransportHandle appTransport, uint32_t pktSize, Rm_PacketHandle *pktHandle)
164 {
165 Rm_Packet *rm_pkt = NULL;
167 rm_pkt = calloc(1, sizeof(*rm_pkt));
168 if (!rm_pkt) {
169 error_msg("Failed to malloc RM packet (err: %s)\n",
170 strerror(errno));
171 return (NULL);
172 }
173 rm_pkt->pktLenBytes = pktSize;
174 *pktHandle = rm_pkt;
176 return(rm_pkt);
177 }
179 void transportFree (Rm_Packet *rm_pkt)
180 {
181 int32_t status;
183 if (rm_pkt) {
184 free (rm_pkt);
185 }
186 }
188 int32_t transportSend (Rm_AppTransportHandle appTransport, Rm_PacketHandle pktHandle)
189 {
190 sock_name_t *client_sock_name = (sock_name_t *)appTransport;
191 Rm_Packet *rm_pkt = (Rm_Packet *)pktHandle;
193 if (sock_send(server_sock, (char *)rm_pkt, (int) rm_pkt->pktLenBytes, client_sock_name)) {
194 error_msg("Failed to send RM packet\n");
195 }
196 else {
197 /* Print resources after sending response */
198 Rm_resourceStatus(server_h, 1);
199 }
201 transportFree(rm_pkt);
202 return (0);
203 }
205 int rm_server_run(void *grl, void *policy, void *lin_dtb, int is_daemon)
206 {
207 Rm_InitCfg rm_init_cfg;
208 Rm_TransportCfg rm_trans_cfg;
209 int32_t rm_result;
210 trans_map_entry_t *trans_map = NULL;
211 trans_map_entry_t *new_map_entry;
212 trans_map_entry_t *map_index;
213 int retval;
214 int length = 0;
215 sock_name_t serv_sock_name;
216 sock_name_t client_sock_addr;
217 Rm_Packet *rm_pkt = NULL;
218 char pkt_src[RM_NAME_MAX_CHARS];
219 struct sockaddr_un client_addr;
220 int signal_fd = -1;
221 char rm_socket_name[] = RM_SERVER_SOCKET_NAME;
223 rmsrv_cfg.logfile_p = fopen(RMSERVER_DAEMON_LOG_FILE_NAME, "w+");
224 if (!rmsrv_cfg.logfile_p) {
225 printf("Error in opening log file %s (%s)", RMSERVER_DAEMON_LOG_FILE_NAME, strerror(errno));
226 }
228 debug_msg("Starting RM server");
230 /* Create the Server instance */
231 memset(&rm_init_cfg, 0, sizeof(rm_init_cfg));
232 rm_init_cfg.instName = server_name;
233 rm_init_cfg.instType = Rm_instType_SERVER;
234 rm_init_cfg.instCfg.serverCfg.globalResourceList = grl;
235 rm_init_cfg.instCfg.serverCfg.linuxDtb = lin_dtb;
236 rm_init_cfg.instCfg.serverCfg.globalPolicy = policy;
237 server_h = Rm_init(&rm_init_cfg, &rm_result);
238 ERROR_CHECK(RM_OK, rm_result, server_name, "Initialization failed\n");
240 debug_msg("RM Server initialized with name: %s", server_name);
242 Rm_resourceStatus(server_h, 1);
244 serv_sock_name.type = sock_name_e;
245 serv_sock_name.s.name = rm_socket_name;
246 server_sock = sock_open (&serv_sock_name);
247 if (!server_sock) {
248 error_msg("Error when opening socket %s", rm_socket_name);
249 return -1;
250 }
252 if (is_daemon){
253 signal_fd = daemon_signal_fd();
254 }
256 while(1) {
257 info_msg("Waiting for messages from Clients\n");
258 retval = sock_wait(server_sock, &length, NULL, signal_fd);
259 if (retval < 0) {
260 error_msg("Error in reading from socket\n");
261 goto loop_continue;
262 }
264 if (length < sizeof(rm_pkt)) {
265 error_msg("invalid RM message length %d\n", length);
266 goto loop_continue;
267 }
268 rm_pkt = calloc(1, length);
269 if (!rm_pkt) {
270 error_msg("can't malloc for recv'd RM message (err: %s)\n",
271 strerror(errno));
272 goto loop_continue;
273 }
275 client_sock_addr.type = sock_addr_e;
276 client_sock_addr.s.addr = &client_addr;
277 retval = sock_recv(server_sock, (char *)rm_pkt, length, &client_sock_addr);
278 if (retval != length) {
279 error_msg("recv RM pkt failed from socket, received = %d, expected = %d\n",
280 retval, length);
281 goto loop_continue;
282 }
284 info_msg("Received RM pkt of size %d bytes from socket %s\n", length, client_sock_addr.s.addr->sun_path);
285 if (Rm_receiveGetPktSrcName(rm_pkt, &pkt_src[0], RM_NAME_MAX_CHARS) == RM_OK) {
286 info_msg(" RM pkt originated from %s instance\n", &pkt_src[0]);
287 }
288 if (Rm_receiveGetPktServiceSrcName(rm_pkt, &pkt_src[0], RM_NAME_MAX_CHARS) == RM_OK) {
289 info_msg(" Service request within RM pkt originated from %s instance\n", &pkt_src[0]);
290 }
292 map_index = trans_map;
293 while(map_index != NULL) {
294 if (strncmp(map_index->remote_sock->s.addr->sun_path,
295 client_addr.sun_path,
296 sizeof(client_addr.sun_path)) == 0) {
297 break;
298 }
299 map_index = map_index->n;
300 }
302 if (!map_index) {
303 new_map_entry = calloc(1, sizeof(*new_map_entry));
304 new_map_entry->remote_sock = calloc(1, sizeof(sock_name_t));
305 new_map_entry->remote_sock->s.addr = calloc(1, sizeof(struct sockaddr_un));
306 new_map_entry->remote_sock->type = sock_addr_e;
307 memcpy(new_map_entry->remote_sock->s.addr, &client_addr, sizeof(struct sockaddr_un));
309 /* Register the Client with the Server instance */
310 rm_trans_cfg.rmHandle = server_h;
311 rm_trans_cfg.appTransportHandle = (Rm_AppTransportHandle)new_map_entry->remote_sock;
312 rm_trans_cfg.remoteInstType = Rm_instType_CLIENT;
313 rm_trans_cfg.transportCallouts.rmAllocPkt = transportAlloc;
314 rm_trans_cfg.transportCallouts.rmSendPkt = transportSend;
315 new_map_entry->trans_handle = Rm_transportRegister(&rm_trans_cfg, &rm_result);
317 new_map_entry->n = NULL;
319 if (trans_map == NULL) {
320 trans_map = new_map_entry;
321 }
322 else {
323 map_index = trans_map;
325 while(map_index->n != NULL) {
326 map_index = map_index->n;
327 }
328 map_index->n = new_map_entry;
329 }
331 map_index = new_map_entry;
332 }
334 /* Provide packet to RM Server for processing */
335 if (rm_result = Rm_receivePacket(map_index->trans_handle, rm_pkt)) {
336 error_msg("RM failed to process received packet: %d\n", rm_result);
337 }
339 loop_continue:
340 /* Cleanups */
341 length = 0;
342 transportFree(rm_pkt);
343 memset(&client_sock_addr, 0, sizeof(sock_name_t));
344 memset(&client_addr, 0, sizeof(struct sockaddr_un));
345 }
346 }
348 char *get_pid_file_name(void) {
349 static char pid_file_name[] = RMSERVER_DAEMON_PID_FILE_NAME;
351 return pid_file_name;
352 }
354 static void print_usage(char *appname)
355 {
356 printf ("Usage: %s [OPTION]... [GRL] [POLICY]\n", appname);
357 printf ("Run a resource manager server with the specified [GRL] and [POLICY]\n"
358 "[GRL] and [POLICY] must be device tree blob (DTB) files\n"
359 "Example: rmserver grl.dtb policy.dtb\n\n"
360 "Configuration:\n"
361 " -s, --logsize MAXLOGSIZE MAXLOGSIZE bytes will be written to the\n"
362 " log file before the log is reset. On\n"
363 " reset, the log file will be wiped.\n"
364 "\n"
365 " The default for MAXLOGSIZE is 8MB if\n"
366 " logsize is not specified\n"
367 "Optional Input:\n"
368 " -l, --lindtb [LINUX_DTB] Optionally, provide a Linux DTB file\n"
369 " that RM will use to reserve resources\n"
370 " for Linux. The GRL must have the\n"
371 " proper Linux DTB resource mappings\n"
372 " for this feature to work\n"
373 "Miscellaneous:\n"
374 " -n, --nodaemon do not daemonize, run in foreground\n"
375 " -k, --kill kill the existing daemon\n"
376 " -h, --help print this message\n"
377 "\n"
378 "rmserver will run as a daemon by default.\n\n");
379 }
381 int main(int argc, char *argv[])
382 {
383 int opt;
384 int daemonize = 1, kill = 0;
385 int fd;
386 struct stat file_stat;
387 pid_t pid;
388 char *grl_file;
389 char *policy_file;
390 char *lin_dtb_file = NULL;
391 void *grl;
392 void *policy;
393 void *lin_dtb = NULL;
395 const struct option longopts[] =
396 {
397 {"nodaemon", no_argument, 0, 'n'},
398 {"kill", no_argument, 0, 'k'},
399 {"help", no_argument, 0, 'h'},
400 {"lindtb", required_argument, 0, 'l'},
401 {"logsize", required_argument, 0, 's'},
402 {0, 0, 0, 0},
403 };
405 rmsrv_cfg.logfile_max_len = DEFAULT_LOG_LEN;
407 while((opt = getopt_long(argc, argv, "nkhl:s:", longopts, NULL)) != -1) {
408 switch (opt) {
409 case 'n':
410 daemonize = 0;
411 break;
412 case 'k':
413 kill = 1;
414 break;
415 case 'l':
416 lin_dtb_file = optarg;
417 break;
418 case 's':
419 rmsrv_cfg.logfile_max_len = strtol(optarg, NULL, 0);
420 break;
421 case 'h':
422 default:
423 print_usage(argv[0]);
424 exit(EXIT_SUCCESS);
425 }
426 }
428 if (!kill) {
429 /* GRL and Policy must always be provided */
430 if (optind == (argc - 2)) {
431 /* First must be GRL */
432 grl_file = argv[optind++];
433 /* Second must be policy */
434 policy_file = argv[optind++];
435 }
436 else {
437 printf("GRL or policy not provided\n\n");
438 print_usage(argv[0]);
439 exit(EXIT_FAILURE);
440 }
442 /* mmap the GRL */
443 fd = open(grl_file, O_RDONLY);
444 if (fd == -1) {
445 printf("Error opening GRL: %s\n", strerror(errno));
446 exit(EXIT_FAILURE);
447 }
448 /* Obtain file size */
449 if (fstat(fd, &file_stat) == -1) {
450 printf("Error getting GRL size\n");
451 exit(EXIT_FAILURE);
452 }
453 grl = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
454 if (grl == MAP_FAILED) {
455 printf("mmap of GRL failed\n");
456 exit(EXIT_FAILURE);
457 }
459 /* mmap the Global Policy */
460 fd = open(policy_file, O_RDONLY);
461 if (fd == -1) {
462 printf("Error opening Global Policy: %s\n", strerror(errno));
463 exit(EXIT_FAILURE);
464 }
465 /* Obtain file size */
466 if (fstat(fd, &file_stat) == -1) {
467 printf("Error getting Global Policy size\n");
468 exit(EXIT_FAILURE);
469 }
470 policy = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
471 if (policy == MAP_FAILED) {
472 printf("mmap of Global Policy failed\n");
473 exit(EXIT_FAILURE);
474 }
476 if (lin_dtb_file) {
477 /* mmap the Linux DTB if it was provided */
478 fd = open(lin_dtb_file, O_RDONLY);
479 if (fd == -1) {
480 printf("Error opening Linux DTB: %s\n", strerror(errno));
481 exit(EXIT_FAILURE);
482 }
483 /* Obtain file size */
484 if (fstat(fd, &file_stat) == -1) {
485 printf("Error getting Linux DTB size\n");
486 exit(EXIT_FAILURE);
487 }
488 lin_dtb = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
489 if (lin_dtb == MAP_FAILED) {
490 printf("mmap of Linux DTB failed\n");
491 exit(EXIT_FAILURE);
492 }
493 }
494 }
496 if (daemonize) {
497 if (kill) {
498 printf("Killing %s\n", argv[0]);
499 }
500 else {
501 printf("Starting %s\n", argv[0]);
502 }
504 /* Reset signal handlers */
505 if (daemon_reset_sigs(-1) < 0) {
506 printf("Failed to reset all signal handlers: %s\n", strerror(errno));
507 exit(EXIT_FAILURE);
508 }
510 /* Unblock signals */
511 if (daemon_unblock_sigs(-1) < 0) {
512 printf("Failed to unblock all signals: %s\n", strerror(errno));
513 exit(EXIT_FAILURE);
514 }
516 if (check_and_create_path (get_pid_file_name()) < 0) {
517 printf("Failed to create pid file path: %s\n", get_pid_file_name());
518 exit(EXIT_FAILURE);
519 }
521 /* set daemon id string */
522 daemon_log_ident = daemon_ident_from_argv0(argv[0]);
523 daemon_pid_file_proc = (daemon_pid_file_proc_t) get_pid_file_name;
525 if (kill) {
526 if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
527 printf("Failed to kill daemon: %s\n", strerror(errno));
528 exit(EXIT_FAILURE);
529 }
530 daemon_pid_file_remove();
531 exit(EXIT_SUCCESS);
532 }
534 /* Single instance */
535 if ((pid = daemon_pid_file_is_running()) >= 0) {
536 printf("Daemon already running on PID file %u\n", pid);
537 exit(EXIT_FAILURE);
538 }
540 if (daemon_retval_init() < 0) {
541 printf("Failed to create pipe.\n");
542 exit(EXIT_FAILURE);
543 }
545 /* Do the fork */
546 if ((pid = daemon_fork()) < 0) {
547 daemon_retval_done();
548 printf("Error in daemon fork %s\n", strerror(errno));
549 exit(EXIT_FAILURE);
550 }
551 else if (pid) { /* The parent */
552 int ret;
554 /* Wait for 20 seconds for the return value passed from the daemon process */
555 if ((ret = daemon_retval_wait(20)) < 0) {
556 printf("Could not receive return value from daemon process: %s\n", strerror(errno));
557 return -1;
558 }
560 printf("Daemon returned %i as return value.\n", ret);
561 return ret;
562 }
564 /* Close FDs */
565 if (daemon_close_all(-1) < 0) {
566 printf("Failed to close all file descriptors: %s\n", strerror(errno));
568 /* Send the error condition to the parent process */
569 daemon_retval_send(1);
570 goto close_n_exit;
571 }
573 /* Create the PID file */
574 if (daemon_pid_file_create() < 0) {
575 printf("Could not create PID file (%s).\n", strerror(errno));
576 daemon_retval_send(2);
577 goto close_n_exit;
578 }
580 /* Initialize signal handling */
581 if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, 0) < 0) {
582 printf("Could not register signal handlers (%s).\n", strerror(errno));
583 daemon_retval_send(3);
584 goto close_n_exit;
585 }
587 /* Send OK to parent process */
588 daemon_retval_send(0);
589 }
591 rm_server_run(grl, policy, lin_dtb, daemonize);
593 close_n_exit:
594 printf("Exiting %s daemon\n", argv[0]);
595 if (daemonize) {
596 daemon_retval_send(255);
597 daemon_signal_done();
598 daemon_pid_file_remove();
599 }
600 }