首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

Go 語言實作 lsof HTTP 服務:透過 REST API 查詢檔案佔用進程

     編輯:關於C

專案簡介

一個極簡 Go HTTP 服務,接收路徑參數,呼叫系統 `lsof` 命令查詢該檔案被哪些進程佔用,回傳佔用進程數量。展示 Go 執行外部命令、管道串接(`lsof | wc -l`)、HTTP 處理的核心模式。

完整代碼

package main

import (
	"fmt"
	"net/http"
	"os/exec"
	"flag"
)

func main() {
	http.HandleFunc("/", readHandle)
	port := flag.Int("port", 2012, "Listen Port")
	flag.Parse()
	addr := fmt.Sprintf(":%d", *port)
	fmt.Printf("Server listening on %s\n", addr)
	http.ListenAndServe(addr, nil)
}

func readHandle(w http.ResponseWriter, r *http.Request) {
	// 1. 獲取查詢參數 path
	path := r.FormValue("path")
	if path == "" {
		http.Error(w, "Missing 'path' parameter", http.StatusBadRequest)
		return
	}

	// 2. 建立命令管道:lsof path | wc -l
	lsofCmd := exec.Command("lsof", path)
	wcCmd := exec.Command("wc", "-l")

	// 3. 串接管道:lsof stdout → wc stdin
	lsofOut, err := lsofCmd.StdoutPipe()
	if err != nil {
		http.Error(w, "Pipe setup failed", http.StatusInternalServerError)
		return
	}
	wcCmd.Stdin = lsofOut

	// 4. 啟動 lsof
	if err := lsofCmd.Start(); err != nil {
		http.Error(w, "lsof start failed", http.StatusInternalServerError)
		return
	}

	// 5. 執行 wc 並獲取輸出(會阻塞直到 lsof 結束)
	out, err := wcCmd.Output()
	if err != nil {
		http.Error(w, "wc execution failed", http.StatusInternalServerError)
		return
	}

	// 6. 等待 lsof 結束(回收資源)
	lsofCmd.Wait()

	// 7. 回傳結果
	fmt.Fprintf(w, "Path: %s\n", path)
	fmt.Fprintf(w, "Process count: %s", string(out))
	fmt.Fprintf(w, "Request URL path: %s\n", r.URL.Path[1:])
}

關鍵技術點解析

技術說明
exec.Command建立外部命令對象,支援參數切片,自動處理路徑搜尋
StdoutPipe()返回連接命令標準輸出的管道(`io.Reader`),啟動命令後可讀取
管道串接將 `lsofCmd.StdoutPipe()` 賦給 `wcCmd.Stdin`,實現 `lsof | wc`
Start() vs Run()`Start()` 非阻塞啟動,需手動 `Wait()`;`Run()` = `Start()` + `Wait()`
Output()運行命令並捕獲標準輸出(內部自動 `Start`+`Wait`),此處用於 `wc`
flag.Int命令列參數解析,預設埠 2012

編譯與執行

# 編譯
go build -o lsof_server main.go

# 執行(預設埠 2012)
./lsof_server

# 或指定埠
./lsof_server -port=8080

測試範例

# 查詢 /var/log/syslog 被誰佔用
curl "https://localhost:2012/?path=/var/log/syslog"

# 輸出範例
Path: /var/log/syslog
Process count: 2
Request URL path: 

安全與健壯性強化(生產環境必做)

  1. 輸入驗證與清洗:`path` 應檢查路徑遍歷(`../`)、命令注入(雖 `exec.Command` 參數化已防注入,但 `lsof` 參數仍可能被濫用)。
  2. 超時控制:使用 `context.WithTimeout` 包裝 `cmd.Run()`,防止 `lsof` 掛起導致服務阻塞。
  3. 錯誤碼區分:`lsof` 找不到檔案返回 1,非錯誤;需檢查 `err.(*exec.ExitError)` 判斷。
  4. 併發限制:`lsof` 較重,建議用 `semaphore` 限制並發數,或改用 `fsnotify`/`inotify` 監控模式。
  5. 權限最小化:服務以非 root 用戶運行,僅授予必要檔案讀取權限。

進階替代方案:純 Go 實作 lsof 功能

避免外部命令依賴,可直接讀取 `/proc//fd/` 目錄掃描所有進程檔案描述符:

func getProcessesHoldingFile(target string) ([]int, error) {
    var pids []int
    entries, _ := os.ReadDir("/proc")
    for _, e := range entries {
        if pid, err := strconv.Atoi(e.Name()); err == nil {
            fdDir := fmt.Sprintf("/proc/%d/fd", pid)
            links, _ := os.ReadDir(fdDir)
            for _, l := range links {
                dest, _ := os.Readlink(fdDir + "/" + l.Name())
                if dest == target {
                    pids = append(pids, pid)
                    break
                }
            }
        }
    }
    return pids, nil
}

優點:無外部依賴、跨平台(Linux)、效率更高。缺點:需讀取 `/proc` 權限、Linux 專用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved