Вариант 1. Выполнение скрипта от имени пользователя, повышение привилегий до уровня root для pacman -U
Я думаю, это плохая идея. Для скриптов немного неряшливо читать пароли и долго таскать их по незащищенной памяти, потому что в конце они хотят выполнить что-то от имени пользователя root. Однако, если вы настаиваете, это будет работать аналогично варианту 2.
Вариант 2. Выполнение сценария от имени пользователя root, понижение уровня подпроцессов до пользователя для git clone
и makepkg
Вы можете использовать опцию preexec_fn
subprocess.Popen
, которая, согласно help(subprocess.Popen)
, делает:
preexec_fn: (только POSIX) Объект, который будет вызываться в дочернем процессе непосредственно перед выполнением дочернего процесса.
Единственная хитрость в этом — передать аргументы. Если бы нам не нужно было этого делать, мы могли бы просто указать функцию (os.setuid
) как опцию preexec_fn
в вызове конструктора Popen()
. К сожалению, тогда он будет выполняться, когда мы определяем подпроцесс, а не тогда, когда мы запускаем его, как предполагалось. Таким образом, в конечном итоге мы понизим уровень главного процесса.
Итак, мы должны определить небольшую функцию-обертку:
def demote():
os.setuid(1000)
и затем можем определить наш подпроцесс:
process = Popen(["git", "clone", git_url, new_package_path], preexec_fn=demote)
process.wait()
Конечно, это нехороший способ сделать это - жестко закодированные uid и все такое. Однако если мы хотим, чтобы demote()
принимал аргументы, мы возвращаемся к исходной точке. Таким образом, мы должны сделать это немного сложнее и определить вложенную функцию (функция внутри функции), где внешняя функция вызывается для определения подпроцесса, принимает аргументы, определяет uid и gid и возвращает внутреннюю функцию, которая применяет понижение. Звучит сложно? Строго говоря, вам не обязательно этого делать, но, допустим, мы хотим быть гибкими, так что начнем:
def demote(user_uid, user_gid):
def apply_demotion():
os.setgid(user_gid)
os.setuid(user_uid)
return apply_demotion
Второй вариант — использовать su -c (command) (user)
, который можно вызвать из python с помощью функции os.system()
:
os.system("su -c makepkg " + user)
Вы, вероятно, захотите запустить команду git clone
и makepkg
одним и тем же методом для согласованности.
Рабочий пример
import os
from subprocess import Popen
import glob
import sys
def clone_and_makepkg(package_name, aur_folder_path="/tmp/build/", uid=1000, gid=1000):
"""prepare urls and paths"""
git_url = "https://aur.archlinux.org/" + package_name + ".git"
new_package_path = os.path.join(aur_folder_path, package_name)
"""ensure the build directory exists and user has correct privileges to work there"""
if not os.path.exists(aur_folder_path):
os.mkdir(aur_folder_path)
os.chmod(aur_folder_path, 0o777)
"""perform git clone"""
print("Cloning " + git_url + " to " + new_package_path)
Popen(["git", "clone", git_url, new_package_path], preexec_fn=demote(uid, gid)).wait()
"""change to make directory"""
os.chdir(new_package_path)
"""run makepkg"""
Popen("makepkg", preexec_fn=demote(uid, gid)).wait()
"""collect built packages"""
built_packages = glob.glob(new_package_path + os.sep + "*.pkg.tar.xz")
"""install each package"""
for package in built_packages:
print("Installing package {}".format(package))
os.system("pacman -U " + package + " --noconfirm")
def demote(user_uid, user_gid):
def apply_demotion():
os.setgid(user_gid)
os.setuid(user_uid)
return apply_demotion
if __name__ == "__main__":
"""Example call. Will by default install package 3to2 (This example does not have any further dependencies
except for python. Which is evidently already installed. So this example is hassle-free. For
examples with dependencies, you may want to find a way to deal with those in the script.)"""
pkgname = "3to2"
if len(sys.argv) > 1:
pkgname = sys.argv[1]
clone_and_makepkg(package_name=pkgname)
person
0range
schedule
30.11.2018