diff --git a/extensions/connect-to-vpn/CHANGELOG.md b/extensions/connect-to-vpn/CHANGELOG.md index 0f0e02f8078..7bd6d1fd240 100644 --- a/extensions/connect-to-vpn/CHANGELOG.md +++ b/extensions/connect-to-vpn/CHANGELOG.md @@ -1,5 +1,9 @@ # Connect to VPN Changelog +## [Bug fix] - 2024-12-22 + +- Fixed spawning zombie process while updating services status in background + ## [New Additions] - 2024-11-13 - Menu bar icon updates in the background diff --git a/extensions/connect-to-vpn/package.json b/extensions/connect-to-vpn/package.json index c820268db0e..81279607c19 100644 --- a/extensions/connect-to-vpn/package.json +++ b/extensions/connect-to-vpn/package.json @@ -8,7 +8,8 @@ "contributors": [ "sato11", "stonerl", - "vyacheslav" + "vyacheslav", + "yshmeel" ], "license": "MIT", "commands": [ diff --git a/extensions/connect-to-vpn/src/menu-bar.tsx b/extensions/connect-to-vpn/src/menu-bar.tsx index 34a8f0c8bfb..40eeb663aab 100644 --- a/extensions/connect-to-vpn/src/menu-bar.tsx +++ b/extensions/connect-to-vpn/src/menu-bar.tsx @@ -1,6 +1,6 @@ import { MenuBarExtra, showToast, Toast, environment, LaunchType } from "@raycast/api"; import { NetworkService, normalizeHardwarePort, openNetworkSettings, useNetworkServices } from "./network-services"; -import { useRef, useEffect } from "react"; +import { useRef, useEffect, useState } from "react"; export default function Command() { const { @@ -14,13 +14,24 @@ export default function Command() { fetchServiceStatus, } = useNetworkServices(); + // Indicates, if we need to keep background process running + // Sets false when `startRefresh` ends + const [isBackgroundRunning, setIsBackgroundRunning] = useState(environment.launchType === LaunchType.Background); const isChecking = useRef(false); useEffect(() => { if (environment.launchType === LaunchType.Background) { - startRefresh(); + // Run these conditions only in background mode + + // If all services are loaded in useNetworkServices - start refreshing + if (!isLoading) { + startRefresh(); + } } - }, [environment.launchType, favoriteServices, otherServices, invalidServices]); + + // Check services array's length because of state changing in `fetchServiceStatus` function + // That function changes `networkServices` state and updates whole effect, if we shallow check array memory address + }, [environment.launchType, favoriteServices.length, otherServices.length, invalidServices.length, isLoading]); const startRefresh = async () => { if (!isChecking.current) { @@ -35,6 +46,9 @@ export default function Command() { } finally { console.log("Resetting isChecking flag"); isChecking.current = false; + + // Stop running background process + setIsBackgroundRunning(false); } } else { console.log("Refresh already in progress, skipping..."); @@ -53,7 +67,7 @@ export default function Command() { {favoriteServices.length > 0 && ( diff --git a/extensions/connect-to-vpn/src/network-services.ts b/extensions/connect-to-vpn/src/network-services.ts index e6e24702baa..1c5eb8dc8b4 100644 --- a/extensions/connect-to-vpn/src/network-services.ts +++ b/extensions/connect-to-vpn/src/network-services.ts @@ -1,6 +1,7 @@ import { useState, useEffect, useMemo } from "react"; import { exec } from "child_process"; import { Icon, LocalStorage, Toast, getPreferenceValues, showToast } from "@raycast/api"; +import { execSync } from "node:child_process"; type Preferences = { hideInvalidDevices: boolean; @@ -70,6 +71,8 @@ export function useNetworkServices() { ...currentServices, [service.id]: { ...service, status }, })); + + return status; } catch (err) { console.error(`Error fetching service status for ${service.name}:`, err); setError(err as Error); @@ -338,27 +341,15 @@ const saveFavoriteOrder = async (order: Record) => { const execPromise = (command: string): Promise => new Promise((resolve, reject) => { - const child = exec(command, (err, stdout, stderr) => { - if (err) { - console.error(`Error executing command: ${command}`, err); - reject(err); - } else if (stderr) { - console.warn(`Command stderr: ${stderr}`); - resolve(stdout.trim()); - } else { - resolve(stdout.trim()); - } - }); - - // Ensure the child process is cleaned up - child.on("exit", (code) => { - console.log(`Command exited with code: ${code}`); - }); + try { + const child = execSync(command); + const result = child.toString(); - child.on("error", (err) => { - console.error(`Failed to start command: ${command}`, err); - reject(err); - }); + resolve(result.trim()); + } catch (e) { + console.error(`Command ${command} thrown an error`, e); + reject(e); + } }); const listNetworkServiceOrder = (): Promise => execPromise("/usr/sbin/networksetup -listnetworkserviceorder");