//-------------------------------------------------------------------------------------------------- ssize_t cgrp_GetProcessesList ( cgrp_SubSys_t subsystem, ///< [IN] Sub-system of the cgroup. const char* cgroupNamePtr, ///< [IN] Name of the cgroup. pid_t* pidListPtr, ///< [OUT] Buffer that will contain the list of PIDs. size_t maxPids ///< [IN] The maximum number of pids pidListPtr can hold. ) { // Open the cgroup's processes file for reading. int fd = OpenCgrpFile(subsystem, cgroupNamePtr, PROCS_FILENAME, O_RDONLY); if (fd < 0) { return LE_FAULT; } size_t numPids = BuildTidList(fd, pidListPtr, maxPids); if (numPids == LE_FAULT) { LE_ERROR("Error reading the '%s' cgroup's tasks.", cgroupNamePtr); } fd_Close(fd); return numPids; }
//-------------------------------------------------------------------------------------------------- static void SendStdPipeToLogDaemon ( proc_Ref_t procRef, ///< [IN] Process that owns the write end of the pipe. int pipefd[2], ///< [IN] Pipe fds. int streamNum ///< [IN] Either STDOUT_FILENO or STDERR_FILENO. ) { if (pipefd[READ_PIPE] != -1) { // Send the read end to the log daemon. The fd is closed once it is sent. if (streamNum == STDOUT_FILENO) { logFd_StdOut(pipefd[READ_PIPE], app_GetName(procRef->appRef), procRef->name, procRef->pid); } else { logFd_StdErr(pipefd[READ_PIPE], app_GetName(procRef->appRef), procRef->name, procRef->pid); } // Close the write end of the pipe because we don't need it. fd_Close(pipefd[WRITE_PIPE]); } }
//-------------------------------------------------------------------------------------------------- void smack_RevokeSubject ( const char* subjectLabelPtr ///< [IN] Subject label. ) { // Open the SMACK revoke file. int fd; do { fd = open(SMACK_REVOKE_FILE, O_WRONLY); } while ( (fd == -1) && (errno == EINTR) ); LE_FATAL_IF(fd == -1, "Could not open %s. %m.\n", SMACK_REVOKE_FILE); // Write the label to the SMACK revoke file. int numBytes = 0; do { numBytes = write(fd, subjectLabelPtr, strlen(subjectLabelPtr)); } while ( (numBytes == -1) && (errno == EINTR) ); LE_FATAL_IF(numBytes < 0, "Could not revoke SMACK label '%s'. %m.", subjectLabelPtr); fd_Close(fd); LE_DEBUG("Revoked SMACK label '%s'.", subjectLabelPtr); }
//-------------------------------------------------------------------------------------------------- bool cgrp_IsEmpty ( cgrp_SubSys_t subsystem, ///< [IN] Sub-system of the cgroup. const char* cgroupNamePtr ///< [IN] Name of the cgroup. ) { // Open the cgroup's tasks file for reading. int fd = OpenCgrpFile(subsystem, cgroupNamePtr, TASKS_FILENAME, O_RDONLY); if (fd < 0) { return false; } // Read a tid from the file. pid_t tid = GetTasksId(fd); fd_Close(fd); if (tid >= 0) { return false; } else if (tid == LE_OUT_OF_RANGE) { // No tasks. return true; } else { LE_ERROR("Error reading the '%s' cgroup's tasks.", cgroupNamePtr); return false; } }
//-------------------------------------------------------------------------------------------------- static le_result_t GetValue ( cgrp_SubSys_t subsystem, ///< [IN] Sub-system of the cgroup. const char* cgroupNamePtr, ///< [IN] Name of the cgroup. const char* fileNamePtr, ///< [IN] File name to read from. char* bufPtr, ///< [OUT] Buffer to store the value in. size_t bufSize ///< [IN] Size of the buffer. ) { // Open the file. int fd = OpenCgrpFile(subsystem, cgroupNamePtr, fileNamePtr, O_RDONLY); if (fd < 0) { return LE_FAULT; } // Read the value from the file. ssize_t numBytesRead; do { numBytesRead = read(fd, bufPtr, bufSize); } while ( (numBytesRead == -1) && (errno == EINTR) ); // Check if the read value is valid. le_result_t result; if (numBytesRead == -1) { LE_ERROR("Could not read file '%s' in cgroup '%s'. %m.", fileNamePtr, cgroupNamePtr); result = LE_FAULT; } else if (numBytesRead == bufSize) { // The value in the file is larger than the provided buffer. Truncate the buffer. bufPtr[bufSize-1] = '\0'; result = LE_OVERFLOW; } else { // Null-terminate the string. bufPtr[numBytesRead] = '\0'; // Remove trailing newline characters. while ((numBytesRead > 0) && (bufPtr[numBytesRead - 1] == '\n')) { numBytesRead--; bufPtr[numBytesRead] = '\0'; } result = LE_OK; } fd_Close(fd); return result; }
//-------------------------------------------------------------------------------------------------- static void RedirectStdStream ( int pipefd[2], ///< [IN] Pipe fds. int streamNum ///< [IN] Either STDOUT_FILENO or STDERR_FILENO. ) { if (pipefd[READ_PIPE] != -1) { // Duplicate the write end of the pipe onto the process' standard stream. LE_FATAL_IF(dup2(pipefd[WRITE_PIPE], streamNum) == -1, "Could not duplicate fd. %m."); // Close the two ends of the pipe because we don't need them. fd_Close(pipefd[READ_PIPE]); fd_Close(pipefd[WRITE_PIPE]); } }
//-------------------------------------------------------------------------------------------------- void fa_event_DestructThread ( event_PerThreadRec_t* portablePerThreadRecPtr ) { event_LinuxPerThreadRec_t* perThreadRecPtr = CONTAINER_OF(portablePerThreadRecPtr, event_LinuxPerThreadRec_t, portablePerThreadRec); // Close the epoll file descriptor. fd_Close(perThreadRecPtr->epollFd); // Close the eventfd for the Event Queue. fd_Close(perThreadRecPtr->eventQueueFd); le_mem_Release(perThreadRecPtr); }
//-------------------------------------------------------------------------------------------------- ssize_t cgrp_SendSig ( cgrp_SubSys_t subsystem, ///< [IN] Sub-system of the cgroup. const char* cgroupNamePtr, ///< [IN] Name of the cgroup. int sig ///< [IN] The signal to send. ) { // Open the cgroup's procs file for reading. int fd = OpenCgrpFile(subsystem, cgroupNamePtr, PROCS_FILENAME, O_RDONLY); if (fd < 0) { return LE_FAULT; } // Iterate over the pids in the procs file. size_t numPids = 0; while (1) { pid_t pid = GetTasksId(fd); if (pid >= 0) { numPids++; kill_SendSig(pid, sig); } else if (pid == LE_OUT_OF_RANGE) { // No more PIDs. break; } else { LE_ERROR("Error reading the '%s' cgroup's tasks.", cgroupNamePtr); fd_Close(fd); return LE_FAULT; } } fd_Close(fd); return numPids; }
//-------------------------------------------------------------------------------------------------- bool smack_HasAccess ( const char* subjectLabelPtr, ///< [IN] Subject label. const char* accessModePtr, ///< [IN] Access mode. const char* objectLabelPtr ///< [IN] Object label. ) { CheckLabel(subjectLabelPtr); CheckLabel(objectLabelPtr); // Create the SMACK rule. char rule[SMACK_RULE_STR_BYTES]; MakeRuleStr(subjectLabelPtr, accessModePtr, objectLabelPtr, rule, sizeof(rule)); // Open the SMACK access file. int fd; do { fd = open(SMACK_ACCESS_FILE, O_RDWR); } while ( (fd == -1) && (errno == EINTR) ); LE_FATAL_IF(fd == -1, "Could not open %s. %m.\n", SMACK_ACCESS_FILE); // Write the rule to the SMACK access file. int numBytes = 0; do { numBytes = write(fd, rule, sizeof(rule)-1); } while ( (numBytes == -1) && (errno == EINTR) ); LE_FATAL_IF(numBytes < 0, "Could not write SMACK rule '%s'. %m.", rule); // Read the SMACK access file to see if access would be granted. char a; do { numBytes = read(fd, &a, 1); } while ( (numBytes == -1) && (errno == EINTR) ); LE_FATAL_IF(numBytes <= 0, "Could not read '%s'. %m.", SMACK_ACCESS_FILE); fd_Close(fd); return (a == '1'); }
//-------------------------------------------------------------------------------------------------- void le_msg_HideService ( le_msg_ServiceRef_t serviceRef ///< [in] Reference to the service. ) //-------------------------------------------------------------------------------------------------- { // Stop monitoring the directory socket. le_event_DeleteFdMonitor(serviceRef->fdMonitorRef); serviceRef->fdMonitorRef = NULL; // Close the connection with the Service Directory. fd_Close(serviceRef->directorySocketFd); serviceRef->directorySocketFd = -1; }
//-------------------------------------------------------------------------------------------------- static le_result_t WriteToFile ( cgrp_SubSys_t subsystem, ///< [IN] Sub-system of the cgroup. const char* cgroupNamePtr, ///< [IN] Name of the cgroup. const char* fileNamePtr, ///< [IN] File to write to. const char* string ///< [IN] String to write into the file. ) { // Get the length of the string. size_t len = strlen(string); LE_ASSERT(len > 0); // Open the file. int fd = OpenCgrpFile(subsystem, cgroupNamePtr, fileNamePtr, O_WRONLY); if (fd < 0) { return LE_FAULT; } // Write the string to the file. le_result_t result = LE_OK; ssize_t numBytesWritten = 0; do { numBytesWritten = write(fd, string, len); } while ((numBytesWritten == -1) && (errno == EINTR)); if (numBytesWritten != len) { LE_ERROR("Could not write '%s' to file '%s' in cgroup '%s'. %m.", string, fileNamePtr, cgroupNamePtr); if (errno == ESRCH) { result = LE_OUT_OF_RANGE; } else { result = LE_FAULT; } } fd_Close(fd); return result; }
//-------------------------------------------------------------------------------------------------- static le_result_t GetProcessNameFromPid ( pid_t pId, ///< [IN] The pid of the process whose name to find char* name, ///< [OUT] A buffer to receive the name of the app size_t length ///< [IN] The size of the buffer that receives the name ) { char pathStr[LIMIT_MAX_PATH_BYTES] = ""; char procPathStr[LIMIT_MAX_PATH_BYTES] = ""; if (name != NULL) { // on linux, /proc/[pid]/cmdline contains the command and arguments separated by '\0's int result = snprintf(pathStr, sizeof(pathStr), "/proc/%d/cmdline", pId); if (result < 0 || result >= LIMIT_MAX_PATH_BYTES) { return LE_NOT_FOUND; } int fd = open(pathStr, O_RDONLY); if (fd) { result = read (fd, procPathStr, LIMIT_MAX_PATH_BYTES); fd_Close(fd); if (result == 0) { return LE_FAULT; } else if (strnlen(procPathStr, LIMIT_MAX_PATH_BYTES) == LIMIT_MAX_PATH_BYTES) { // We need the first parameter of the command line, which is path to a process. // This shouldn't be longer than LIMIT_MAX_PATH_BYTES. return LE_OVERFLOW; } // strip the path char* procNamePtr = le_path_GetBasenamePtr(procPathStr, "/"); return le_utf8_Copy(name, procNamePtr, length, NULL); } } else { return LE_FAULT; } return LE_OK; }
//-------------------------------------------------------------------------------------------------- static le_result_t GetProcessNameFromPid ( pid_t pId, ///< [IN] The pid of the process whose name to find char* name, ///< [OUT] A buffer to receive the name of the app size_t length ///< [IN] The size of the buffer that receives the name ) { char pathStr[LIMIT_MAX_PATH_BYTES]; if (name != NULL) { name[0] = '\0'; // on linux, /proc/[pid]/cmdline contains the command and arguments separated by '\0's int result = snprintf(pathStr, sizeof(pathStr), "/proc/%d/cmdline", pId); if (result < 0 || result >= LIMIT_MAX_PATH_BYTES) { return LE_NOT_FOUND; } int fd = open(pathStr, O_RDONLY); if (fd) { result = read (fd, name, length); fd_Close(fd); if (result == 0) { return LE_FAULT; } else if (result == LIMIT_MAX_PATH_BYTES) { name[length - 1] = '\0'; return LE_OVERFLOW; } // strip the path char* procNamePtr = le_path_GetBasenamePtr(name, "/"); // memmove is safe for overlapping memory!! memmove(name, procNamePtr, strlen(procNamePtr) + 1); } } else { return LE_FAULT; } return LE_OK; }
//-------------------------------------------------------------------------------------------------- static void SetSmackNetlabelExceptions ( void ) { // Open the calling process's smack file. int fd; do { fd = open(SMACK_NETLABEL_FILE, O_WRONLY); } while ( (fd == -1) && (errno == EINTR) ); LE_FATAL_IF(fd == -1, "Could not open %s. %m.\n", SMACK_NETLABEL_FILE); // Write netlabel to the file. int result; size_t netlabelExceptionSize; do { netlabelExceptionSize = strlen("127.0.0.1 -CIPSO"); result = write(fd, "127.0.0.1 -CIPSO", netlabelExceptionSize); } while ( (result == -1) && (errno == EINTR) ); LE_FATAL_IF(result != netlabelExceptionSize, "Could not write to %s. %m.\n", SMACK_NETLABEL_FILE); do { netlabelExceptionSize = strlen("0.0.0.0/0 @"); result = write(fd, "0.0.0.0/0 @", netlabelExceptionSize); } while ( (result == -1) && (errno == EINTR) ); LE_FATAL_IF(result != netlabelExceptionSize, "Could not write to %s. %m.\n", SMACK_NETLABEL_FILE); fd_Close(fd); }
//-------------------------------------------------------------------------------------------------- void smack_SetRule ( const char* subjectLabelPtr, ///< [IN] Subject label. const char* accessModePtr, ///< [IN] Access mode. See the function description for details. const char* objectLabelPtr ///< [IN] Object label. ) { CheckLabel(subjectLabelPtr); CheckLabel(objectLabelPtr); // Create the SMACK rule. char rule[SMACK_RULE_STR_BYTES]; MakeRuleStr(subjectLabelPtr, accessModePtr, objectLabelPtr, rule, sizeof(rule)); // Open the SMACK load file. int fd; do { fd = open(SMACK_LOAD_FILE, O_WRONLY); } while ( (fd == -1) && (errno == EINTR) ); LE_FATAL_IF(fd == -1, "Could not open %s. %m.\n", SMACK_LOAD_FILE); // Write the rule to the SMACK load file. int numBytes = 0; size_t ruleLength = strlen(rule); do { numBytes = write(fd, rule, ruleLength); } while ( (numBytes == -1) && (errno == EINTR) ); LE_FATAL_IF(numBytes != ruleLength, "Could not write SMACK rule '%s'. %m.", rule); fd_Close(fd); LE_DEBUG("Set SMACK rule '%s'.", rule); }
//-------------------------------------------------------------------------------------------------- le_result_t smack_GetProcLabel ( pid_t pid, ///< [IN] PID of the process. char* bufPtr, ///< [OUT] Buffer to store the proc's SMACK label. size_t bufSize ///< [IN] Size of the buffer. ) { // Get the process's smack file name. char smackFile[LIMIT_MAX_PATH_BYTES]; LE_ASSERT(snprintf(smackFile, sizeof(smackFile), "/proc/%d/attr/current", pid) < sizeof(smackFile)); // Open the process's smack file. int fd; do { fd = open(smackFile, O_RDONLY); } while ( (fd == -1) && (errno == EINTR) ); if (fd == -1) { LE_ERROR("Could not open %s. %m.\n", smackFile); return LE_FAULT; } // Read the smack label. le_result_t result = fd_ReadLine(fd, bufPtr, bufSize); fd_Close(fd); if ( (result == LE_OUT_OF_RANGE) || (result == LE_FAULT) ) { return LE_FAULT; } return result; }
//-------------------------------------------------------------------------------------------------- void smack_SetMyLabel ( const char* labelPtr ///< [IN] Label to set the calling process to. ) { CheckLabel(labelPtr); // Open the calling process's smack file. int fd; do { fd = open(PROC_SMACK_FILE, O_WRONLY); } while ( (fd == -1) && (errno == EINTR) ); LE_FATAL_IF(fd == -1, "Could not open %s. %m.\n", PROC_SMACK_FILE); // Write the label to the file. size_t labelSize = strlen(labelPtr); int result; do { result = write(fd, labelPtr, labelSize); } while ( (result == -1) && (errno == EINTR) ); LE_FATAL_IF(result != labelSize, "Could not write to %s. %m.\n", PROC_SMACK_FILE); fd_Close(fd); LE_DEBUG("Setting process' SMACK label to '%s'.", labelPtr); }
//-------------------------------------------------------------------------------------------------- static inline le_result_t StartProc ( proc_Ref_t procRef, ///< [IN] The process to start. const char* workingDirPtr, ///< [IN] The path to the process's working directory, relative /// to the sandbox directory. uid_t uid, ///< [IN] The user ID to start the process as. gid_t gid, ///< [IN] The primary group ID for this process. const gid_t* groupsPtr, ///< [IN] List of supplementary groups for this process. size_t numGroups, ///< [IN] The number of groups in the supplementary groups list. const char* sandboxDirPtr ///< [IN] The path to the root of the sandbox this process is to /// run in. If NULL then process will be unsandboxed. ) { if (procRef->pid != -1) { LE_ERROR("Process '%s' (PID: %d) cannot be started because it is already running.", procRef->name, procRef->pid); return LE_FAULT; } // Create a pipe for parent/child synchronization. int syncPipeFd[2]; LE_FATAL_IF(pipe(syncPipeFd) == -1, "Could not create synchronization pipe. %m."); // @Note The current IPC system does not support forking so any reads to the config DB must be // done in the parent process. // Get the environment variables from the config tree for this process. EnvVar_t envVars[LIMIT_MAX_NUM_ENV_VARS]; int numEnvVars = GetEnvironmentVariables(procRef, envVars, LIMIT_MAX_NUM_ENV_VARS); if (numEnvVars == LE_FAULT) { LE_ERROR("Error getting environment variables. Process '%s' cannot be started.", procRef->name); return LE_FAULT; } // Get the command line arguments from the config tree for this process. char argsBuffers[LIMIT_MAX_NUM_CMD_LINE_ARGS][LIMIT_MAX_ARGS_STR_BYTES]; char* argsPtr[NUM_ARGS_PTRS]; if (GetArgs(procRef, argsBuffers, argsPtr) != LE_OK) { LE_ERROR("Could not get command line arguments, process '%s' cannot be started.", procRef->name); return LE_FAULT; } // Get the smack label for the process. // Must get the label here because smack_GetAppLabel uses the config and we can not // use any IPC after a fork. char smackLabel[LIMIT_MAX_SMACK_LABEL_BYTES]; appSmack_GetLabel(app_GetName(procRef->appRef), smackLabel, sizeof(smackLabel)); // Create pipes for the process's standard error and standard out streams. int stderrPipe[2]; int stdoutPipe[2]; CreatePipe(procRef, stderrPipe, STDERR_FILENO); CreatePipe(procRef, stdoutPipe, STDOUT_FILENO); // Create the child process pid_t pID = fork(); if (pID < 0) { LE_EMERG("Failed to fork. %m."); return LE_FAULT; } if (pID == 0) { // Wait for the parent to allow us to continue by blocking on the read pipe until it // is closed. fd_Close(syncPipeFd[WRITE_PIPE]); ssize_t numBytesRead; int dummyBuf; do { numBytesRead = read(syncPipeFd[READ_PIPE], &dummyBuf, 1); } while ( ((numBytesRead == -1) && (errno == EINTR)) || (numBytesRead != 0) ); LE_FATAL_IF(numBytesRead == -1, "Could not read synchronization pipe. %m."); // The parent has allowed us to continue. // Redirect the process's standard streams. RedirectStdStream(stderrPipe, STDERR_FILENO); RedirectStdStream(stdoutPipe, STDOUT_FILENO); // Set the process's SMACK label. smack_SetMyLabel(smackLabel); // Set the umask so that files are not accidentally created with global permissions. umask(S_IRWXG | S_IRWXO); // Unblock all signals that might have been blocked. sigset_t sigSet; LE_ASSERT(sigfillset(&sigSet) == 0); LE_ASSERT(pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL) == 0); SetEnvironmentVariables(envVars, numEnvVars); // Setup the process environment. if (sandboxDirPtr != NULL) { // Sandbox the process. sandbox_ConfineProc(sandboxDirPtr, uid, gid, groupsPtr, numGroups, workingDirPtr); } else { ConfigNonSandboxedProcess(workingDirPtr); } // Launch the child program. This should not return unless there was an error. LE_INFO("Execing '%s'", argsPtr[0]); // Close all non-standard file descriptors. fd_CloseAllNonStd(); execvp(argsPtr[0], &(argsPtr[1])); // The program could not be started. Log an error message. log_ReInit(); LE_FATAL("Could not exec '%s'. %m.", argsPtr[0]); } procRef->pid = pID; procRef->paused = false; // Don't need this end of the pipe. fd_Close(syncPipeFd[READ_PIPE]); // Set the scheduling priority for the child process while the child process is blocked. SetSchedulingPriority(procRef); // Send standard pipes to the log daemon so they will show up in the logs. SendStdPipeToLogDaemon(procRef, stderrPipe, STDERR_FILENO); SendStdPipeToLogDaemon(procRef, stdoutPipe, STDOUT_FILENO); // Set the resource limits for the child process while the child process is blocked. if (resLim_SetProcLimits(procRef) != LE_OK) { LE_ERROR("Could not set the resource limits. %m."); kill_Hard(procRef->pid); } LE_INFO("Starting process %s with pid %d", procRef->name, procRef->pid); // Unblock the child process. fd_Close(syncPipeFd[WRITE_PIPE]); return LE_OK; }
//-------------------------------------------------------------------------------------------------- static void StartDaemon ( DaemonObj_t* daemonPtr ///< [IN] The daemon to start. ) { const char* daemonNamePtr = le_path_GetBasenamePtr(daemonPtr->path, "/"); // Kill all other instances of this process just in case. kill_ByName(daemonNamePtr); // Create a synchronization pipe. int syncPipeFd[2]; LE_FATAL_IF(pipe(syncPipeFd) != 0, "Could not create synchronization pipe. %m."); // Fork a process. pid_t pid = fork(); LE_FATAL_IF(pid < 0, "Failed to fork child process. %m."); if (pid == 0) { // Clear the signal mask so the child does not inherit our signal mask. sigset_t sigSet; LE_ASSERT(sigfillset(&sigSet) == 0); LE_ASSERT(pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL) == 0); // The child does not need the read end of the pipe so close it. fd_Close(syncPipeFd[0]); // Duplicate the write end of the pipe on standard in so the execed program will know // where it is. if (syncPipeFd[1] != STDIN_FILENO) { int r; do { r = dup2(syncPipeFd[1], STDIN_FILENO); } while ( (r == -1) && (errno == EINTR) ); LE_FATAL_IF(r == -1, "Failed to duplicate fd. %m."); // Close the duplicate fd. fd_Close(syncPipeFd[1]); } // Close all non-standard fds. fd_CloseAllNonStd(); smack_SetMyLabel("framework"); // Launch the child program. This should not return unless there was an error. execl(daemonPtr->path, daemonNamePtr, (char*)NULL); // The program could not be started. LE_FATAL("'%s' could not be started: %m", daemonPtr->path); } // Store the pid of the running daemon process. daemonPtr->pid = pid; // Close the write end of the pipe because the parent does not need it. fd_Close(syncPipeFd[1]); // Wait for the child process to close the read end of the pipe. This ensures that the // framework daemons start in the proper order. // TODO: Add a timeout here. ssize_t numBytesRead; int dummyBuf; do { numBytesRead = read(syncPipeFd[0], &dummyBuf, 1); } while ( ((numBytesRead == -1) && (errno == EINTR)) || (numBytesRead != 0) ); LE_FATAL_IF(numBytesRead == -1, "Could not read synchronization pipe. %m."); // Close the read end of the pipe because it is no longer used. fd_Close(syncPipeFd[0]); LE_INFO("Started system process '%s' with PID: %d.", daemonNamePtr, pid); }
//-------------------------------------------------------------------------------------------------- le_result_t addr_GetLibDataSection ( pid_t pid, ///< [IN] Pid to get the address for. Zero means the calling process. const char* libNamePtr, ///< [IN] Name of the library. off_t* libAddressPtr ///< [OUT] Address of the .data section of the library. ) { // Build the path to the maps file. char fileName[LIMIT_MAX_PATH_BYTES]; size_t snprintfSize; if (pid == 0) { snprintfSize = snprintf(fileName, sizeof(fileName), "/proc/self/maps"); } else { snprintfSize = snprintf(fileName, sizeof(fileName), "/proc/%d/maps", pid); } if (snprintfSize >= sizeof(fileName)) { LE_ERROR("Path to file '%s' is too long.", fileName); return LE_FAULT; } // Open the maps file. int fd = open(fileName, O_RDONLY); if (fd == -1) { LE_ERROR("Could not open %s. %m.", fileName); return LE_FAULT; } // Search each line of the file to find the liblegato.so section. off_t address; char line[LIMIT_MAX_PATH_BYTES * 2]; while (1) { le_result_t result = fd_ReadLine(fd, line, sizeof(line)); if (result == LE_OK) { // The line is the section we are looking for if it specifies our library and has an // access mode of "rw-p" which we infer is the text section. if ( (strstr(line, libNamePtr) != NULL) && (strstr(line, "rw-p") != NULL) ) { // The line should begin with the starting address of the section. // Convert it to an address value. char* endPtr; errno = 0; address = strtoll(line, &endPtr, 16); if ( (errno != 0) || (endPtr == line) ) { LE_ERROR("Error reading file %s.", fileName); fd_Close(fd); return LE_FAULT; } // Got the address. break; } } else if (result == LE_OUT_OF_RANGE) { // The end of the file is reached. return LE_NOT_FOUND; } else { LE_ERROR("Error reading '%s' while looking for '%s'.", fileName, libNamePtr); fd_Close(fd); return LE_FAULT; } } fd_Close(fd); *libAddressPtr = address; return LE_OK; }