bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result) { CString binaryPath = fileSystemRepresentation(executablePathOfPluginProcess()); CString pluginPathCString = fileSystemRepresentation(pluginPath); char* argv[4]; argv[0] = const_cast<char*>(binaryPath.data()); argv[1] = const_cast<char*>("-scanPlugin"); argv[2] = const_cast<char*>(pluginPathCString.data()); argv[3] = 0; gint status; gchar* stdOut; if (!g_spawn_sync(0, argv, 0, G_SPAWN_STDERR_TO_DEV_NULL, 0, 0, &stdOut, 0, &status, 0)) return false; if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) return false; const unsigned kNumLinesExpected = 3; String lines[kNumLinesExpected]; unsigned lineIndex = 0; const UChar* current = reinterpret_cast<const UChar*>(stdOut); while (lineIndex < kNumLinesExpected) { const UChar* start = current; while (*current++ != UChar('\n')) { } lines[lineIndex++] = String(start, current - start - 1); } result.name.swap(lines[0]); result.description.swap(lines[1]); result.mimeDescription.swap(lines[2]); return !result.mimeDescription.isEmpty(); }
bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result) { QFileInfo pluginFileInfo(pluginPath); if (!pluginFileInfo.exists()) return false; MetaDataResult::Tag metaDataResult = tryReadPluginMetaDataFromCacheFile(pluginFileInfo.canonicalFilePath(), result); if (metaDataResult == MetaDataResult::Available) return true; if (metaDataResult == MetaDataResult::Unloadable) return false; // Scan the plugin via the plugin process. QString commandLine = QString(executablePathOfPluginProcess()) % QLatin1Char(' ') % QStringLiteral("-scanPlugin") % QLatin1Char(' ') % pluginFileInfo.canonicalFilePath(); QProcess process; process.setReadChannel(QProcess::StandardOutput); process.start(commandLine); bool ranSuccessfully = process.waitForFinished() && process.exitStatus() == QProcess::NormalExit && process.exitCode() == EXIT_SUCCESS; if (ranSuccessfully) { QByteArray outputBytes = process.readAll(); ASSERT(!(outputBytes.size() % sizeof(UChar))); String output(reinterpret_cast<const UChar*>(outputBytes.constData()), outputBytes.size() / sizeof(UChar)); Vector<String> lines; output.split(UChar('\n'), true, lines); ASSERT(lines.size() == 3); result.name.swap(lines[0]); result.description.swap(lines[1]); result.mimeDescription.swap(lines[2]); } else process.kill(); QVariantMap map; map[QStringLiteral("path")] = QString(pluginFileInfo.canonicalFilePath()); map[QStringLiteral("timestamp")] = QDateTime::currentDateTime().toString(); if (!ranSuccessfully || result.mimeDescription.isEmpty()) { // We failed getting the meta data in some way. Cache this information, so we don't // need to rescan such plugins every time. We will retry it once the plugin is updated. map[QStringLiteral("unloadable")] = QStringLiteral("true"); appendToCacheFile(QJsonObject::fromVariantMap(map)); return false; } map[QStringLiteral("name")] = QString(result.name); map[QStringLiteral("description")] = QString(result.description); map[QStringLiteral("mimeDescription")] = QString(result.mimeDescription); appendToCacheFile(QJsonObject::fromVariantMap(map)); return true; }
bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result) { #if PLATFORM(GTK) || PLATFORM(EFL) CString binaryPath = fileSystemRepresentation(executablePathOfPluginProcess()); CString pluginPathCString = fileSystemRepresentation(pluginPath); char* argv[4]; argv[0] = const_cast<char*>(binaryPath.data()); argv[1] = const_cast<char*>("-scanPlugin"); argv[2] = const_cast<char*>(pluginPathCString.data()); argv[3] = 0; int status; GUniqueOutPtr<char> stdOut; // If the disposition of SIGCLD signal is set to SIG_IGN (default) // then the signal will be ignored and g_spawn_sync() will not be // able to return the status. // As a consequence, we make sure that the disposition is set to // SIG_DFL before calling g_spawn_sync(). #if defined(SIGCLD) struct sigaction action; sigaction(SIGCLD, 0, &action); if (action.sa_handler == SIG_IGN) { action.sa_handler = SIG_DFL; sigaction(SIGCLD, &action, 0); } #endif if (!g_spawn_sync(0, argv, 0, G_SPAWN_STDERR_TO_DEV_NULL, 0, 0, &stdOut.outPtr(), 0, &status, 0)) return false; if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS || !stdOut) return false; String stdOutString = String::fromUTF8(stdOut.get()); Vector<String> lines; stdOutString.split(UChar('\n'), true, lines); if (lines.size() < 3) return false; result.name.swap(lines[0]); result.description.swap(lines[1]); result.mimeDescription.swap(lines[2]); #if PLATFORM(GTK) if (lines.size() > 3) result.requiresGtk2 = lines[3] == "requires-gtk2"; #endif return !result.mimeDescription.isEmpty(); #else // PLATFORM(GTK) || PLATFORM(EFL) return false; #endif // PLATFORM(GTK) || PLATFORM(EFL) }
bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result) { #if PLATFORM(GTK) || (PLATFORM(EFL) && ENABLE(GLIB_SUPPORT)) CString binaryPath = fileSystemRepresentation(executablePathOfPluginProcess()); CString pluginPathCString = fileSystemRepresentation(pluginPath); char* argv[4]; argv[0] = const_cast<char*>(binaryPath.data()); argv[1] = const_cast<char*>("-scanPlugin"); argv[2] = const_cast<char*>(pluginPathCString.data()); argv[3] = 0; int status; char* stdOut = 0; // If the disposition of SIGCLD signal is set to SIG_IGN (default) // then the signal will be ignored and g_spawn_sync() will not be // able to return the status. // As a consequence, we make sure that the disposition is set to // SIG_DFL before calling g_spawn_sync(). struct sigaction action; sigaction(SIGCLD, 0, &action); if (action.sa_handler == SIG_IGN) { action.sa_handler = SIG_DFL; sigaction(SIGCLD, &action, 0); } if (!g_spawn_sync(0, argv, 0, G_SPAWN_STDERR_TO_DEV_NULL, 0, 0, &stdOut, 0, &status, 0)) return false; if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS || !stdOut) { free(stdOut); return false; } String stdOutString(reinterpret_cast<const UChar*>(stdOut)); free(stdOut); Vector<String> lines; stdOutString.split(UChar('\n'), lines); if (lines.size() < 3) return false; result.name.swap(lines[0]); result.description.swap(lines[1]); result.mimeDescription.swap(lines[2]); return !result.mimeDescription.isEmpty(); #else // PLATFORM(GTK) || (PLATFORM(EFL) && ENABLE(GLIB_SUPPORT)) return false; #endif // PLATFORM(GTK) || (PLATFORM(EFL) && ENABLE(GLIB_SUPPORT)) }
void ProcessLauncher::launchProcess() { int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { ASSERT_NOT_REACHED(); return; } pid_t pid = fork(); if (!pid) { // child process close(sockets[1]); String socket = String::format("%d", sockets[0]); String executablePath; switch (m_launchOptions.processType) { case WebProcess: executablePath = executablePathOfWebProcess(); break; case PluginProcess: executablePath = executablePathOfPluginProcess(); break; default: ASSERT_NOT_REACHED(); return; } #ifndef NDEBUG if (m_launchOptions.processCmdPrefix.isEmpty()) #endif execl(executablePath.utf8().data(), executablePath.utf8().data(), socket.utf8().data(), static_cast<char*>(0)); #ifndef NDEBUG else { String cmd = makeString(m_launchOptions.processCmdPrefix, ' ', executablePath, ' ', socket); if (system(cmd.utf8().data()) == -1) { ASSERT_NOT_REACHED(); return; } } #endif } else if (pid > 0) { // parent process; close(sockets[0]); m_processIdentifier = pid; // We've finished launching the process, message back to the main run loop. RunLoop::main()->dispatch(bind(&ProcessLauncher::didFinishLaunchingProcess, this, pid, sockets[1])); } else { ASSERT_NOT_REACHED(); return; } }
void ProcessLauncher::launchProcess() { int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { ASSERT_NOT_REACHED(); return; } String processCmdPrefix, executablePath, pluginPath; switch (m_launchOptions.processType) { case WebProcess: executablePath = executablePathOfWebProcess(); break; #if ENABLE(PLUGIN_PROCESS) case PluginProcess: executablePath = executablePathOfPluginProcess(); pluginPath = m_launchOptions.extraInitializationData.get("plugin-path"); break; #endif default: ASSERT_NOT_REACHED(); return; } #ifndef NDEBUG if (!m_launchOptions.processCmdPrefix.isEmpty()) processCmdPrefix = m_launchOptions.processCmdPrefix; #endif Vector<OwnArrayPtr<char>> args = createArgsArray(processCmdPrefix, executablePath, String::number(sockets[0]), pluginPath); // Do not perform memory allocation in the middle of the fork() // exec() below. FastMalloc can potentially deadlock because // the fork() doesn't inherit the running threads. pid_t pid = fork(); if (!pid) { // Child process. close(sockets[1]); execvp(args.data()[0].get(), reinterpret_cast<char* const*>(args.data())); } else if (pid > 0) { // parent process; close(sockets[0]); m_processIdentifier = pid; // We've finished launching the process, message back to the main run loop. RunLoop::main()->dispatch(bind(&ProcessLauncher::didFinishLaunchingProcess, this, pid, sockets[1])); } else { ASSERT_NOT_REACHED(); return; } }
void ProcessLauncher::launchProcess() { GPid pid = 0; int sockets[2]; if (socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) < 0) { g_printerr("Creation of socket failed: %s.\n", g_strerror(errno)); ASSERT_NOT_REACHED(); return; } String executablePath, pluginPath; CString realExecutablePath, realPluginPath; if (m_launchOptions.processType == WebProcess) executablePath = executablePathOfWebProcess(); else { executablePath = executablePathOfPluginProcess(); pluginPath = m_launchOptions.extraInitializationData.get("plugin-path"); realPluginPath = fileSystemRepresentation(pluginPath); } realExecutablePath = fileSystemRepresentation(executablePath); GOwnPtr<gchar> socket(g_strdup_printf("%d", sockets[0])); char* argv[4]; argv[0] = const_cast<char*>(realExecutablePath.data()); argv[1] = socket.get(); argv[2] = const_cast<char*>(realPluginPath.data()); argv[3] = 0; GOwnPtr<GError> error; int spawnFlags = G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD; if (!g_spawn_async(0, argv, 0, static_cast<GSpawnFlags>(spawnFlags), childSetupFunction, GINT_TO_POINTER(sockets[1]), &pid, &error.outPtr())) { g_printerr("Unable to fork a new WebProcess: %s.\n", error->message); ASSERT_NOT_REACHED(); } close(sockets[0]); m_processIdentifier = pid; // Monitor the child process, it calls waitpid to prevent the child process from becomming a zombie, // and it allows us to close the socket when the child process crashes. g_child_watch_add(m_processIdentifier, childFinishedFunction, GINT_TO_POINTER(sockets[1])); // We've finished launching the process, message back to the main run loop. RunLoop::main()->dispatch(bind(&ProcessLauncher::didFinishLaunchingProcess, this, m_processIdentifier, sockets[1])); }
bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result) { #if PLATFORM(GTK) || PLATFORM(EFL) String pluginProcessPath = executablePathOfPluginProcess(); #if PLATFORM(GTK) bool requiresGtk2 = pluginRequiresGtk2(pluginPath); if (requiresGtk2) #if ENABLE(PLUGIN_PROCESS_GTK2) pluginProcessPath.append('2'); #else return false; #endif #endif CString binaryPath = fileSystemRepresentation(pluginProcessPath); CString pluginPathCString = fileSystemRepresentation(pluginPath); char* argv[4]; argv[0] = const_cast<char*>(binaryPath.data()); argv[1] = const_cast<char*>("-scanPlugin"); argv[2] = const_cast<char*>(pluginPathCString.data()); argv[3] = nullptr; // If the disposition of SIGCLD signal is set to SIG_IGN (default) // then the signal will be ignored and g_spawn_sync() will not be // able to return the status. // As a consequence, we make sure that the disposition is set to // SIG_DFL before calling g_spawn_sync(). #if defined(SIGCLD) struct sigaction action; sigaction(SIGCLD, 0, &action); if (action.sa_handler == SIG_IGN) { action.sa_handler = SIG_DFL; sigaction(SIGCLD, &action, 0); } #endif int status; GUniqueOutPtr<char> stdOut; GUniqueOutPtr<GError> error; if (!g_spawn_sync(nullptr, argv, nullptr, G_SPAWN_STDERR_TO_DEV_NULL, nullptr, nullptr, &stdOut.outPtr(), nullptr, &status, &error.outPtr())) { WTFLogAlways("Failed to launch %s: %s", argv[0], error->message); return false; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { WTFLogAlways("Error scanning plugin %s, %s returned %d exit status", argv[2], argv[0], status); return false; } if (!stdOut) { WTFLogAlways("Error scanning plugin %s, %s didn't write any output to stdout", argv[2], argv[0]); return false; } Vector<String> lines; String::fromUTF8(stdOut.get()).split(UChar('\n'), true, lines); if (lines.size() < 3) { WTFLogAlways("Error scanning plugin %s, too few lines of output provided", argv[2]); return false; } result.name.swap(lines[0]); result.description.swap(lines[1]); result.mimeDescription.swap(lines[2]); #if PLATFORM(GTK) result.requiresGtk2 = requiresGtk2; #endif return !result.mimeDescription.isEmpty(); #else // PLATFORM(GTK) || PLATFORM(EFL) return false; #endif // PLATFORM(GTK) || PLATFORM(EFL) }
void ProcessLauncher::launchProcess() { int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { ASSERT_NOT_REACHED(); return; } CString executablePath, pluginPath; switch (m_launchOptions.processType) { case WebProcess: executablePath = executablePathOfWebProcess().utf8(); break; #if ENABLE(PLUGIN_PROCESS) case PluginProcess: executablePath = executablePathOfPluginProcess().utf8(); pluginPath = m_launchOptions.extraInitializationData.get("plugin-path").utf8(); break; #endif default: ASSERT_NOT_REACHED(); return; } char socket[5]; snprintf(socket, sizeof(socket), "%d", sockets[0]); #ifndef NDEBUG CString prefixedExecutablePath; if (!m_launchOptions.processCmdPrefix.isEmpty()) { String prefixedExecutablePathStr = m_launchOptions.processCmdPrefix + ' ' + String::fromUTF8(executablePath.data()) + ' ' + socket + ' ' + String::fromUTF8(pluginPath.data()); prefixedExecutablePath = prefixedExecutablePathStr.utf8(); } #endif // Do not perform memory allocation in the middle of the fork() // exec() below. FastMalloc can potentially deadlock because // the fork() doesn't inherit the running threads. pid_t pid = fork(); if (!pid) { // Child process. close(sockets[1]); #ifndef NDEBUG if (!prefixedExecutablePath.isNull()) { // FIXME: This is not correct because it invokes the shell // and keeps this process waiting. Should be changed to // something like execvp(). if (system(prefixedExecutablePath.data()) == -1) { ASSERT_NOT_REACHED(); exit(EXIT_FAILURE); } else exit(EXIT_SUCCESS); } #endif execl(executablePath.data(), executablePath.data(), socket, pluginPath.data(), static_cast<char*>(0)); } else if (pid > 0) { // parent process; close(sockets[0]); m_processIdentifier = pid; // We've finished launching the process, message back to the main run loop. RunLoop::main()->dispatch(bind(&ProcessLauncher::didFinishLaunchingProcess, this, pid, sockets[1])); } else { ASSERT_NOT_REACHED(); return; } }