samba.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #!/usr/bin/env bash
  2. #===============================================================================
  3. # FILE: samba.sh
  4. #
  5. # USAGE: ./samba.sh
  6. #
  7. # DESCRIPTION: Entrypoint for samba docker container
  8. #
  9. # OPTIONS: ---
  10. # REQUIREMENTS: ---
  11. # BUGS: ---
  12. # NOTES: ---
  13. # AUTHOR: David Personette ([email protected]),
  14. # ORGANIZATION:
  15. # CREATED: 09/28/2014 12:11
  16. # REVISION: 1.0
  17. #===============================================================================
  18. set -o nounset # Treat unset variables as an error
  19. ### charmap: setup character mapping for file/directory names
  20. # Arguments:
  21. # chars) from:to character mappings separated by ','
  22. # Return: configured character mapings
  23. charmap() { local chars="$1" file=/etc/samba/smb.conf
  24. grep -q catia $file || sed -i '/TCP_NODELAY/a \
  25. \
  26. vfs objects = catia\
  27. catia:mappings =\
  28. ' $file
  29. sed -i '/catia:mappings/s| =.*| = '"$chars"'|' $file
  30. }
  31. ### generic: set a generic config option in a section
  32. # Arguments:
  33. # section) section of config file
  34. # option) raw option
  35. # Return: line added to smb.conf (replaces existing line with same key)
  36. generic() { local section="$1" key="$(sed 's| *=.*||' <<< $2)" \
  37. value="$(sed 's|[^=]*= *||' <<< $2)" file=/etc/samba/smb.conf
  38. if sed -n '/^\['"$section"'\]/,/^\[/p' $file | grep -qE '^;*\s*'"$key"; then
  39. sed -i '/^\['"$1"'\]/,/^\[/s|^;*\s*\('"$key"' = \).*| \1'"$value"'|' \
  40. "$file"
  41. else
  42. sed -i '/\['"$section"'\]/a \ '"$key = $value" "$file"
  43. fi
  44. }
  45. ### global: set a global config option
  46. # Arguments:
  47. # option) raw option
  48. # Return: line added to smb.conf (replaces existing line with same key)
  49. global() { local key="$(sed 's| *=.*||' <<< $1)" \
  50. value="$(sed 's|[^=]*= *||' <<< $1)" file=/etc/samba/smb.conf
  51. if sed -n '/^\[global\]/,/^\[/p' $file | grep -qE '^;*\s*'"$key"; then
  52. sed -i '/^\[global\]/,/^\[/s|^;*\s*\('"$key"' = \).*| \1'"$value"'|' \
  53. "$file"
  54. else
  55. sed -i '/\[global\]/a \ '"$key = $value" "$file"
  56. fi
  57. }
  58. ### include: add a samba config file include
  59. # Arguments:
  60. # file) file to import
  61. include() { local includefile="$1" file=/etc/samba/smb.conf
  62. sed -i "\\|include = $includefile|d" "$file"
  63. echo "include = $includefile" >> "$file"
  64. }
  65. ### import: import a smbpasswd file
  66. # Arguments:
  67. # file) file to import
  68. # Return: user(s) added to container
  69. import() { local file="$1" name id
  70. while read name id; do
  71. grep -q "^$name:" /etc/passwd || adduser -D -H -u "$id" "$name"
  72. done < <(cut -d: -f1,2 $file | sed 's/:/ /')
  73. pdbedit -i smbpasswd:$file
  74. }
  75. ### perms: fix ownership and permissions of share paths
  76. # Arguments:
  77. # none)
  78. # Return: result
  79. perms() { local i file=/etc/samba/smb.conf
  80. for i in $(awk -F ' = ' '/ path = / {print $2}' $file); do
  81. chown -Rh smbuser. $i
  82. find $i -type d ! -perm 775 -exec chmod 775 {} \;
  83. find $i -type f ! -perm 0664 -exec chmod 0664 {} \;
  84. done
  85. }
  86. export -f perms
  87. ### recycle: disable recycle bin
  88. # Arguments:
  89. # none)
  90. # Return: result
  91. recycle() { local file=/etc/samba/smb.conf
  92. sed -i '/recycle:/d; /vfs objects/s/ recycle / /' $file
  93. }
  94. ### share: Add share
  95. # Arguments:
  96. # share) share name
  97. # path) path to share
  98. # browsable) 'yes' or 'no'
  99. # readonly) 'yes' or 'no'
  100. # guest) 'yes' or 'no'
  101. # users) list of allowed users
  102. # admins) list of admin users
  103. # writelist) list of users that can write to a RO share
  104. # comment) description of share
  105. # Return: result
  106. share() { local share="$1" path="$2" browsable="${3:-yes}" ro="${4:-yes}" \
  107. guest="${5:-yes}" users="${6:-""}" admins="${7:-""}" \
  108. writelist="${8:-""}" comment="${9:-""}" file=/etc/samba/smb.conf
  109. sed -i "/\\[$share\\]/,/^\$/d" $file
  110. echo "[$share]" >>$file
  111. echo " path = $path" >>$file
  112. echo " browsable = $browsable" >>$file
  113. echo " read only = $ro" >>$file
  114. echo " guest ok = $guest" >>$file
  115. [[ ${VETO:-yes} == no ]] || {
  116. echo -n " veto files = /.apdisk/.DS_Store/.TemporaryItems/" >>$file
  117. echo -n ".Trashes/desktop.ini/ehthumbs.db/Network Trash Folder/" >>$file
  118. echo "Temporary Items/Thumbs.db/" >>$file
  119. echo " delete veto files = yes" >>$file
  120. }
  121. [[ ${users:-""} && ! ${users:-""} == all ]] &&
  122. echo " valid users = $(tr ',' ' ' <<< $users)" >>$file
  123. [[ ${admins:-""} && ! ${admins:-""} =~ none ]] &&
  124. echo " admin users = $(tr ',' ' ' <<< $admins)" >>$file
  125. [[ ${writelist:-""} && ! ${writelist:-""} =~ none ]] &&
  126. echo " write list = $(tr ',' ' ' <<< $writelist)" >>$file
  127. [[ ${comment:-""} && ! ${comment:-""} =~ none ]] &&
  128. echo " comment = $(tr ',' ' ' <<< $comment)" >>$file
  129. echo "" >>$file
  130. [[ -d $path ]] || mkdir -p $path
  131. }
  132. ### smb: disable SMB2 minimum
  133. # Arguments:
  134. # none)
  135. # Return: result
  136. smb() { local file=/etc/samba/smb.conf
  137. sed -i 's/\([^#]*min protocol *=\).*/\1 LANMAN1/' $file
  138. }
  139. ### user: add a user
  140. # Arguments:
  141. # name) for user
  142. # password) for user
  143. # id) for user
  144. # group) for user
  145. # gid) for group
  146. # Return: user added to container
  147. user() { local name="$1" passwd="$2" id="${3:-""}" group="${4:-""}" \
  148. gid="${5:-""}"
  149. [[ "$group" ]] && { grep -q "^$group:" /etc/group ||
  150. addgroup ${gid:+--gid $gid }"$group"; }
  151. grep -q "^$name:" /etc/passwd ||
  152. adduser -D -H ${group:+-G $group} ${id:+-u $id} "$name"
  153. echo -e "$passwd\n$passwd" | smbpasswd -s -a "$name"
  154. }
  155. ### workgroup: set the workgroup
  156. # Arguments:
  157. # workgroup) the name to set
  158. # Return: configure the correct workgroup
  159. workgroup() { local workgroup="$1" file=/etc/samba/smb.conf
  160. sed -i 's|^\( *workgroup = \).*|\1'"$workgroup"'|' $file
  161. }
  162. ### widelinks: allow access wide symbolic links
  163. # Arguments:
  164. # none)
  165. # Return: result
  166. widelinks() { local file=/etc/samba/smb.conf \
  167. replace='\1\n wide links = yes\n unix extensions = no'
  168. sed -i 's/\(follow symlinks = yes\)/'"$replace"'/' $file
  169. }
  170. ### usage: Help
  171. # Arguments:
  172. # none)
  173. # Return: Help text
  174. usage() { local RC="${1:-0}"
  175. echo "Usage: ${0##*/} [-opt] [command]
  176. Options (fields in '[]' are optional, '<>' are required):
  177. -h This help
  178. -c \"<from:to>\" setup character mapping for file/directory names
  179. required arg: \"<from:to>\" character mappings separated by ','
  180. -G \"<section;parameter>\" Provide generic section option for smb.conf
  181. required arg: \"<section>\" - IE: \"share\"
  182. required arg: \"<parameter>\" - IE: \"log level = 2\"
  183. -g \"<parameter>\" Provide global option for smb.conf
  184. required arg: \"<parameter>\" - IE: \"log level = 2\"
  185. -i \"<path>\" Import smbpassword
  186. required arg: \"<path>\" - full file path in container
  187. -n Start the 'nmbd' daemon to advertise the shares
  188. -p Set ownership and permissions on the shares
  189. -r Disable recycle bin for shares
  190. -S Disable SMB2 minimum version
  191. -s \"<name;/path>[;browse;readonly;guest;users;admins;writelist;comment]\"
  192. Configure a share
  193. required arg: \"<name>;</path>\"
  194. <name> is how it's called for clients
  195. <path> path to share
  196. NOTE: for the default value, just leave blank
  197. [browsable] default:'yes' or 'no'
  198. [readonly] default:'yes' or 'no'
  199. [guest] allowed default:'yes' or 'no'
  200. NOTE: for user lists below, usernames are separated by ','
  201. [users] allowed default:'all' or list of allowed users
  202. [admins] allowed default:'none' or list of admin users
  203. [writelist] list of users that can write to a RO share
  204. [comment] description of share
  205. -u \"<username;password>[;ID;group;GID]\" Add a user
  206. required arg: \"<username>;<passwd>\"
  207. <username> for user
  208. <password> for user
  209. [ID] for user
  210. [group] for user
  211. [GID] for group
  212. -w \"<workgroup>\" Configure the workgroup (domain) samba should use
  213. required arg: \"<workgroup>\"
  214. <workgroup> for samba
  215. -W Allow access wide symbolic links
  216. -I Add an include option at the end of the smb.conf
  217. required arg: \"<include file path>\"
  218. <include file path> in the container, e.g. a bind mount
  219. The 'command' (if provided and valid) will be run instead of samba
  220. " >&2
  221. exit $RC
  222. }
  223. [[ "${USERID:-""}" =~ ^[0-9]+$ ]] && usermod -u $USERID -o smbuser
  224. [[ "${GROUPID:-""}" =~ ^[0-9]+$ ]] && groupmod -g $GROUPID -o smb
  225. while getopts ":hc:G:g:i:nprs:Su:Ww:I:" opt; do
  226. case "$opt" in
  227. h) usage ;;
  228. c) charmap "$OPTARG" ;;
  229. G) eval generic $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $OPTARG) ;;
  230. g) global "$OPTARG" ;;
  231. i) import "$OPTARG" ;;
  232. n) NMBD="true" ;;
  233. p) PERMISSIONS="true" ;;
  234. r) recycle ;;
  235. s) eval share $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $OPTARG) ;;
  236. S) smb ;;
  237. u) eval user $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $OPTARG) ;;
  238. w) workgroup "$OPTARG" ;;
  239. W) widelinks ;;
  240. I) include "$OPTARG" ;;
  241. "?") echo "Unknown option: -$OPTARG"; usage 1 ;;
  242. ":") echo "No argument value for option: -$OPTARG"; usage 2 ;;
  243. esac
  244. done
  245. shift $(( OPTIND - 1 ))
  246. [[ "${CHARMAP:-""}" ]] && charmap "$CHARMAP"
  247. while read i; do
  248. eval generic $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $i)
  249. done < <(env | awk '/^GENERIC[0-9=_]/ {sub (/^[^=]*=/, "", $0); print}')
  250. while read i; do
  251. global "$i"
  252. done < <(env | awk '/^GLOBAL[0-9=_]/ {sub (/^[^=]*=/, "", $0); print}')
  253. [[ "${IMPORT:-""}" ]] && import "$IMPORT"
  254. [[ "${RECYCLE:-""}" ]] && recycle
  255. while read i; do
  256. eval share $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $i)
  257. done < <(env | awk '/^SHARE[0-9=_]/ {sub (/^[^=]*=/, "", $0); print}')
  258. [[ "${SMB:-""}" ]] && smb
  259. while read i; do
  260. eval user $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $i)
  261. done < <(env | awk '/^USER[0-9=_]/ {sub (/^[^=]*=/, "", $0); print}')
  262. [[ "${WORKGROUP:-""}" ]] && workgroup "$WORKGROUP"
  263. [[ "${WIDELINKS:-""}" ]] && widelinks
  264. [[ "${INCLUDE:-""}" ]] && include "$INCLUDE"
  265. [[ "${PERMISSIONS:-""}" ]] && perms &
  266. if [[ $# -ge 1 && -x $(which $1 2>&-) ]]; then
  267. exec "$@"
  268. elif [[ $# -ge 1 ]]; then
  269. echo "ERROR: command not found: $1"
  270. exit 13
  271. elif ps -ef | egrep -v grep | grep -q smbd; then
  272. echo "Service already running, please restart container to apply changes"
  273. else
  274. [[ ${NMBD:-""} ]] && ionice -c 3 nmbd -D
  275. exec ionice -c 3 smbd --foreground --no-process-group </dev/null
  276. fi