@@ -2,7 +2,10 @@ package sys
22
33import (
44 "fmt"
5+ "os"
56 "os/exec"
7+ "os/user"
8+ "strconv"
69 "strings"
710)
811
@@ -14,3 +17,76 @@ func Command(name string, arg ...string) (string, error) {
1417 }
1518 return strings .TrimSpace (string (out )), nil
1619}
20+
21+ // CommandAsUser выполняет команду от имени указанного пользователя
22+ // Используется для установки системного прокси для конкретного пользователя
23+ func CommandAsUser (username string , name string , arg ... string ) (string , error ) {
24+ // Если username пустой или текущий пользователь уже нужный, выполняем напрямую
25+ if username == "" {
26+ return Command (name , arg ... )
27+ }
28+
29+ currentUser , err := user .Current ()
30+ if err == nil && currentUser .Username == username {
31+ return Command (name , arg ... )
32+ }
33+
34+ // Получаем информацию о пользователе
35+ targetUser , err := user .Lookup (username )
36+ if err != nil {
37+ return "" , fmt .Errorf ("user %q not found: %w" , username , err )
38+ }
39+
40+ // Получаем UID пользователя для запуска D-Bus сессии
41+ uid , err := strconv .Atoi (targetUser .Uid )
42+ if err != nil {
43+ return "" , fmt .Errorf ("invalid UID for user %q: %w" , username , err )
44+ }
45+
46+ // Определяем DBUS_SESSION_BUS_ADDRESS для пользователя
47+ // Обычно это unix:path=/run/user/{UID}/bus
48+ dbusAddr := fmt .Sprintf ("unix:path=/run/user/%d/bus" , uid )
49+
50+ // Пробуем использовать runuser (не требует пароля от root) или sudo
51+ // runuser доступен по умолчанию в большинстве дистрибутивов
52+ var cmdName string
53+ var cmdArgs []string
54+
55+ // Проверяем, запущены ли мы от root
56+ if currentUser != nil && (currentUser .Username == "root" || currentUser .Uid == "0" ) {
57+ // От root используем runuser (не требует пароля)
58+ cmdName = "runuser"
59+ cmdArgs = []string {
60+ "-u" , username ,
61+ "--" ,
62+ "env" ,
63+ "DBUS_SESSION_BUS_ADDRESS=" + dbusAddr ,
64+ "HOME=" + targetUser .HomeDir ,
65+ "USER=" + username ,
66+ name ,
67+ }
68+ } else {
69+ // Не от root - используем sudo с -n (может потребовать настройки sudoers)
70+ cmdName = "sudo"
71+ cmdArgs = []string {
72+ "-n" , // non-interactive mode
73+ "-u" , username ,
74+ "env" ,
75+ "DBUS_SESSION_BUS_ADDRESS=" + dbusAddr ,
76+ "HOME=" + targetUser .HomeDir ,
77+ "USER=" + username ,
78+ name ,
79+ }
80+ }
81+
82+ cmdArgs = append (cmdArgs , arg ... )
83+
84+ c := exec .Command (cmdName , cmdArgs ... )
85+ // Наследуем переменные окружения
86+ c .Env = os .Environ ()
87+ out , err := c .CombinedOutput ()
88+ if err != nil {
89+ return "" , fmt .Errorf ("%s as %q: %w: %q" , strings .Join (append ([]string {cmdName , "-u" , username , name }, arg ... ), " " ), username , err , out )
90+ }
91+ return strings .TrimSpace (string (out )), nil
92+ }
0 commit comments