runtime_init.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import subprocess
  2. from openhands.core.logger import openhands_logger as logger
  3. def init_user_and_working_directory(
  4. username: str, user_id: int, initial_pwd: str
  5. ) -> int | None:
  6. """Create working directory and user if not exists.
  7. It performs the following steps effectively:
  8. * Creates the Working Directory:
  9. - Uses mkdir -p to create the directory.
  10. - Sets ownership to username:root.
  11. - Adjusts permissions to be readable and writable by group and others.
  12. * User Verification and Creation:
  13. - Checks if the user exists using id -u.
  14. - If the user exists with the correct UID, it skips creation.
  15. - If the UID differs, it logs a warning and return an updated user_id.
  16. - If the user doesn't exist, it proceeds to create the user.
  17. * Sudo Configuration:
  18. - Appends %sudo ALL=(ALL) NOPASSWD:ALL to /etc/sudoers to grant
  19. passwordless sudo access to the sudo group.
  20. - Adds the user to the sudo group with the useradd command, handling
  21. UID conflicts by incrementing the UID if necessary.
  22. Args:
  23. username (str): The username to create.
  24. user_id (int): The user ID to assign to the user.
  25. initial_pwd (str): The initial working directory to create.
  26. Returns:
  27. int | None: The user ID if it was updated, None otherwise.
  28. """
  29. # First create the working directory, independent of the user
  30. logger.debug(f'Client working directory: {initial_pwd}')
  31. command = f'umask 002; mkdir -p {initial_pwd}'
  32. output = subprocess.run(command, shell=True, capture_output=True)
  33. out_str = output.stdout.decode()
  34. command = f'chown -R {username}:root {initial_pwd}'
  35. output = subprocess.run(command, shell=True, capture_output=True)
  36. out_str += output.stdout.decode()
  37. command = f'chmod g+rw {initial_pwd}'
  38. output = subprocess.run(command, shell=True, capture_output=True)
  39. out_str += output.stdout.decode()
  40. logger.debug(f'Created working directory. Output: [{out_str}]')
  41. # Skip root since it is already created
  42. if username == 'root':
  43. return None
  44. # Check if the username already exists
  45. existing_user_id = -1
  46. try:
  47. result = subprocess.run(
  48. f'id -u {username}', shell=True, check=True, capture_output=True
  49. )
  50. existing_user_id = int(result.stdout.decode().strip())
  51. # The user ID already exists, skip setup
  52. if existing_user_id == user_id:
  53. logger.debug(
  54. f'User `{username}` already has the provided UID {user_id}. Skipping user setup.'
  55. )
  56. else:
  57. logger.warning(
  58. f'User `{username}` already exists with UID {existing_user_id}. Skipping user setup.'
  59. )
  60. return existing_user_id
  61. return None
  62. except subprocess.CalledProcessError as e:
  63. # Returncode 1 indicates, that the user does not exist yet
  64. if e.returncode == 1:
  65. logger.debug(
  66. f'User `{username}` does not exist. Proceeding with user creation.'
  67. )
  68. else:
  69. logger.error(f'Error checking user `{username}`, skipping setup:\n{e}\n')
  70. raise
  71. # Add sudoer
  72. sudoer_line = r"echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
  73. output = subprocess.run(sudoer_line, shell=True, capture_output=True)
  74. if output.returncode != 0:
  75. raise RuntimeError(f'Failed to add sudoer: {output.stderr.decode()}')
  76. logger.debug(f'Added sudoer successfully. Output: [{output.stdout.decode()}]')
  77. command = (
  78. f'useradd -rm -d /home/{username} -s /bin/bash '
  79. f'-g root -G sudo -u {user_id} {username}'
  80. )
  81. output = subprocess.run(command, shell=True, capture_output=True)
  82. if output.returncode == 0:
  83. logger.debug(
  84. f'Added user `{username}` successfully with UID {user_id}. Output: [{output.stdout.decode()}]'
  85. )
  86. else:
  87. raise RuntimeError(
  88. f'Failed to create user `{username}` with UID {user_id}. Output: [{output.stderr.decode()}]'
  89. )
  90. return None