kanrisuru 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +47 -0
  4. data/.rubocop_todo.yml +0 -0
  5. data/CHANGELOG.md +0 -0
  6. data/Gemfile +2 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +143 -7
  9. data/Rakefile +5 -3
  10. data/bin/console +4 -3
  11. data/kanrisuru.gemspec +21 -12
  12. data/lib/kanrisuru.rb +41 -2
  13. data/lib/kanrisuru/command.rb +99 -0
  14. data/lib/kanrisuru/core.rb +53 -0
  15. data/lib/kanrisuru/core/archive.rb +154 -0
  16. data/lib/kanrisuru/core/disk.rb +302 -0
  17. data/lib/kanrisuru/core/file.rb +332 -0
  18. data/lib/kanrisuru/core/find.rb +108 -0
  19. data/lib/kanrisuru/core/group.rb +97 -0
  20. data/lib/kanrisuru/core/ip.rb +1032 -0
  21. data/lib/kanrisuru/core/mount.rb +138 -0
  22. data/lib/kanrisuru/core/path.rb +140 -0
  23. data/lib/kanrisuru/core/socket.rb +168 -0
  24. data/lib/kanrisuru/core/stat.rb +104 -0
  25. data/lib/kanrisuru/core/stream.rb +121 -0
  26. data/lib/kanrisuru/core/system.rb +348 -0
  27. data/lib/kanrisuru/core/transfer.rb +203 -0
  28. data/lib/kanrisuru/core/user.rb +198 -0
  29. data/lib/kanrisuru/logger.rb +8 -0
  30. data/lib/kanrisuru/mode.rb +277 -0
  31. data/lib/kanrisuru/os_package.rb +235 -0
  32. data/lib/kanrisuru/remote.rb +10 -0
  33. data/lib/kanrisuru/remote/cluster.rb +95 -0
  34. data/lib/kanrisuru/remote/cpu.rb +68 -0
  35. data/lib/kanrisuru/remote/env.rb +33 -0
  36. data/lib/kanrisuru/remote/file.rb +354 -0
  37. data/lib/kanrisuru/remote/fstab.rb +412 -0
  38. data/lib/kanrisuru/remote/host.rb +191 -0
  39. data/lib/kanrisuru/remote/memory.rb +19 -0
  40. data/lib/kanrisuru/remote/os.rb +87 -0
  41. data/lib/kanrisuru/result.rb +78 -0
  42. data/lib/kanrisuru/template.rb +32 -0
  43. data/lib/kanrisuru/util.rb +40 -0
  44. data/lib/kanrisuru/util/bits.rb +203 -0
  45. data/lib/kanrisuru/util/fs_mount_opts.rb +655 -0
  46. data/lib/kanrisuru/util/os_family.rb +213 -0
  47. data/lib/kanrisuru/util/signal.rb +161 -0
  48. data/lib/kanrisuru/version.rb +3 -1
  49. data/spec/functional/core/archive_spec.rb +228 -0
  50. data/spec/functional/core/disk_spec.rb +80 -0
  51. data/spec/functional/core/file_spec.rb +341 -0
  52. data/spec/functional/core/find_spec.rb +52 -0
  53. data/spec/functional/core/group_spec.rb +65 -0
  54. data/spec/functional/core/ip_spec.rb +71 -0
  55. data/spec/functional/core/path_spec.rb +93 -0
  56. data/spec/functional/core/socket_spec.rb +31 -0
  57. data/spec/functional/core/stat_spec.rb +98 -0
  58. data/spec/functional/core/stream_spec.rb +99 -0
  59. data/spec/functional/core/system_spec.rb +96 -0
  60. data/spec/functional/core/transfer_spec.rb +108 -0
  61. data/spec/functional/core/user_spec.rb +76 -0
  62. data/spec/functional/os_package_spec.rb +75 -0
  63. data/spec/functional/remote/cluster_spec.rb +45 -0
  64. data/spec/functional/remote/cpu_spec.rb +41 -0
  65. data/spec/functional/remote/env_spec.rb +36 -0
  66. data/spec/functional/remote/fstab_spec.rb +76 -0
  67. data/spec/functional/remote/host_spec.rb +68 -0
  68. data/spec/functional/remote/memory_spec.rb +29 -0
  69. data/spec/functional/remote/os_spec.rb +63 -0
  70. data/spec/functional/remote/remote_file_spec.rb +180 -0
  71. data/spec/helper/test_hosts.rb +68 -0
  72. data/spec/hosts.json +92 -0
  73. data/spec/spec_helper.rb +11 -3
  74. data/spec/unit/fstab_spec.rb +22 -0
  75. data/spec/unit/kanrisuru_spec.rb +9 -0
  76. data/spec/unit/mode_spec.rb +183 -0
  77. data/spec/unit/template_spec.rb +13 -0
  78. data/spec/unit/util_spec.rb +177 -0
  79. data/spec/zz_reboot_spec.rb +46 -0
  80. metadata +136 -13
  81. data/spec/kanrisuru_spec.rb +0 -9
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module Kanrisuru
6
+ module Core
7
+ module Transfer
8
+ extend OsPackage::Define
9
+
10
+ os_define :linux, :download
11
+ os_define :linux, :upload
12
+ os_define :linux, :wget
13
+
14
+ WGET_FILENAME_MODES = %w[unix windows nocontrol ascii lowercase uppercase].freeze
15
+ WGET_SSL_PROTO = %w[auto SSLv2 SSLv3 TLSv1].freeze
16
+
17
+ def download(remote_path, local_path = nil, opts = {})
18
+ if local_path.instance_of?(Hash)
19
+ opts = local_path
20
+ local_path = nil
21
+ end
22
+
23
+ tmp_path = "/tmp/kanrisuru-tmp-#{Time.now.to_i}-#{object_id}"
24
+
25
+ begin
26
+ result = cp(remote_path, tmp_path, force: true)
27
+ raise 'Unable to copy remote file to temp path' unless result.success?
28
+
29
+ result = ssh.scp.download!(tmp_path, local_path, opts)
30
+ Kanrisuru::Util.blank?(local_path) ? result : local_path
31
+ ensure
32
+ rm(tmp_path, force: true) if inode?(tmp_path)
33
+ end
34
+ end
35
+
36
+ def upload(local_path, remote_path, opts = {})
37
+ tmp_path = "/tmp/kanrisuru-tmp-#{Time.now.to_i}-#{object_id}"
38
+
39
+ begin
40
+ result = ssh.scp.upload!(local_path, tmp_path, opts)
41
+ raise 'Unable to upload file' unless result
42
+
43
+ result = mv(tmp_path, remote_path)
44
+ raise 'Unable to move file to remote path' unless result.success?
45
+
46
+ stat(remote_path)
47
+ ensure
48
+ rm(tmp_path, force: true) if inode?(tmp_path)
49
+ end
50
+ end
51
+
52
+ def wget(url, opts = {})
53
+ command = Kanrisuru::Command.new('wget')
54
+
55
+ ## Logging and input
56
+ command.append_flag('--quiet', opts[:quiet])
57
+
58
+ case opts[:verbose]
59
+ when true
60
+ command.append_flag('--verbose')
61
+ when false
62
+ command.append_flag('--no-verbose')
63
+ end
64
+
65
+ command.append_arg('--output-file', opts[:log_file])
66
+ command.append_arg('--append-output', opts[:append_log_file])
67
+
68
+ ## Download
69
+ command.append_arg('--bind-address', opts[:bind_address])
70
+ command.append_arg('--tries', opts[:retries])
71
+ command.append_arg('--output-document', opts[:output_document])
72
+ command.append_flag('--no-clobber', opts[:no_clobber])
73
+ command.append_flag('--continue', opts[:continue])
74
+ command.append_flag('--server-response', opts[:server_response])
75
+ command.append_flag('--spider', opts[:spider])
76
+ command.append_arg('--timeout', opts[:timeout])
77
+ command.append_arg('--dns-timeout', opts[:dns_timeout])
78
+ command.append_arg('--connect-timeout', opts[:connect_timeout])
79
+ command.append_arg('--read-timeout', opts[:read_timeout])
80
+ command.append_arg('--limit-rate', opts[:limit_rate])
81
+ command.append_arg('--wait', opts[:wait])
82
+ command.append_arg('--waitretry', opts[:waitretry])
83
+ command.append_flag('--random-wait', opts[:random_wait])
84
+ command.append_flag('--no-proxy', opts[:no_proxy])
85
+ command.append_flag('--no-dns-cache', opts[:no_dns_cache])
86
+
87
+ command.append_arg('--quota', opts[:quota])
88
+
89
+ if Kanrisuru::Util.present?(opts[:restrict_file_names])
90
+ command.append_arg('--restrict-file-names', Kanrisuru::Util.string_join_array(opts[:restrict_file_names]))
91
+ end
92
+
93
+ case opts[:family]
94
+ when 'inet'
95
+ command.append_flag('--inet4-only')
96
+ when 'inet6'
97
+ command.append_flag('--inet6-only')
98
+ end
99
+
100
+ command.append_flag('--retry-connrefused', opts[:retry_connrefused])
101
+ command.append_arg('--user', opts[:user])
102
+ command.append_arg('--password', opts[:password])
103
+ command.append_flag('--no-iri', opts[:no_iri])
104
+ command.append_arg('--local-encoding', opts[:local_encoding])
105
+ command.append_arg('--remote-encoding', opts[:remote_encoding])
106
+
107
+ ## Directories
108
+ command.append_flag('--no-directories', opts[:no_directories])
109
+ command.append_flag('--force-directories', opts[:force_directories])
110
+ command.append_flag('--no-host-directories', opts[:no_host_directories])
111
+ command.append_flag('--protocol-directories', opts[:protocol_directories])
112
+ command.append_arg('--cut-dirs', opts[:cut_dirs])
113
+ command.append_arg('--directory-prefix', opts[:directory_prefix])
114
+
115
+ ## HTTP
116
+ command.append_arg('--default-page', opts[:default_page])
117
+ command.append_flag('--adjust-extension', opts[:adjust_extension])
118
+ command.append_arg('--http-user', opts[:http_user])
119
+ command.append_arg('--http-password', opts[:http_password])
120
+ command.append_arg('--load-cookies', opts[:load_cookies])
121
+ command.append_arg('--save-cookies', opts[:save_cookies])
122
+ command.append_flag('--no-http-keep-alive', opts[:no_http_keep_alive])
123
+ command.append_flag('--no-cache', opts[:no_cache])
124
+ command.append_flag('--no-cookies', opts[:no_cookies])
125
+ command.append_flag('--keep-session-cookies', opts[:keep_session_cookies])
126
+ command.append_flag('--ignore-length', opts[:ignore_length])
127
+ command.append_arg('--header', opts[:header])
128
+ command.append_arg('--max-redirect', opts[:max_redirect])
129
+ command.append_arg('--proxy-user', opts[:proxy_user])
130
+ command.append_arg('--proxy-password', opts[:proxy_password])
131
+ command.append_arg('--referer', opts[:referer])
132
+ command.append_flag('--save-headers', opts[:save_headers])
133
+ command.append_arg('--user-agent', opts[:user_agent])
134
+
135
+ post_data = opts[:post_data]
136
+
137
+ if Kanrisuru::Util.present?(post_data)
138
+ post_data = post_data.instance_of?(Hash) ? URI.encode_www_form(post_data) : post_data
139
+ command.append_arg('--post-data', post_data)
140
+ end
141
+
142
+ command.append_arg('--post-file', opts[:post_file])
143
+ command.append_flag('--content-disposition', opts[:content_disposition])
144
+
145
+ ## SSL / TLS
146
+ if Kanrisuru::Util.present?(opts[:secure_protocol])
147
+ raise ArgumentError, 'invalid ssl protocol' unless WGET_SSL_PROTO.include?(opts[:secure_protocol])
148
+
149
+ command.append_arg('--secure-protocol', opts[:secure_protocol])
150
+ end
151
+
152
+ command.append_flag('--no-check-certificate', opts[:no_check_certificate])
153
+ command.append_arg('--certificate', opts[:certificate])
154
+ command.append_arg('--certificate-type', opts[:certificate_type])
155
+ command.append_arg('--private-key', opts[:private_key])
156
+ command.append_arg('--private-key-type', opts[:private_key_type])
157
+ command.append_arg('--ca-certificate', opts[:ca_certificate])
158
+ command.append_arg('--ca-directory', opts[:ca_directory])
159
+ command.append_arg('--random-file', opts[:random_file])
160
+ command.append_arg('--egd-file', opts[:egd_file])
161
+
162
+ ## FTP
163
+ command.append_arg('--ftp-user', opts[:ftp_user])
164
+ command.append_arg('--ftp-password', opts[:ftp_password])
165
+ command.append_flag('--no-remove-listing', opts[:no_remove_listing])
166
+ command.append_flag('--no-glob', opts[:no_glob])
167
+ command.append_flag('--no-passive-ftp', opts[:no_passive_ftp])
168
+ command.append_flag('--retr-symlinks', opts[:retr_symlinks])
169
+
170
+ ## Recursive Retrieval
171
+ command.append_flag('--recursive', opts[:recursive])
172
+ command.append_arg('--level', opts[:depth])
173
+ command.append_flag('--delete-after', opts[:delete_after])
174
+ command.append_flag('--convert-links', opts[:convert_links])
175
+ command.append_flag('--backup-converted', opts[:backup_converted])
176
+ command.append_flag('--mirror', opts[:mirror])
177
+ command.append_flag('--page-requisites', opts[:page_requisites])
178
+ command.append_flag('--strict-comments', opts[:strict_comments])
179
+
180
+ ## Recursive Accept/Reject
181
+ command.append_arg('--accept', Kanrisuru::Util.array_join_string(opts[:accept_list]))
182
+ command.append_arg('--reject', Kanrisuru::Util.array_join_string(opts[:reject_list]))
183
+ command.append_arg('--domains', Kanrisuru::Util.array_join_string(opts[:domain_list]))
184
+ command.append_arg('--exclude-domains', Kanrisuru::Util.array_join_string(opts[:exclude_domain_list]))
185
+ command.append_arg('--follow-tags', Kanrisuru::Util.array_join_string(opts[:follow_tags]))
186
+ command.append_arg('--ignore-tags', Kanrisuru::Util.array_join_string(opts[:ignore_tags]))
187
+ command.append_arg('--include-directories', Kanrisuru::Util.array_join_string(opts[:include_directories]))
188
+ command.append_arg('--exclude-directories', Kanrisuru::Util.array_join_string(opts[:exclude_directories]))
189
+ command.append_flag('--follow-ftp', opts[:follow_ftp])
190
+ command.append_flag('--ignore-case', opts[:ignore_case])
191
+ command.append_flag('--span-hosts', opts[:span_hosts])
192
+ command.append_flag('--relative', opts[:relative])
193
+ command.append_flag('--no-parent', opts[:no_parent])
194
+
195
+ command << url
196
+
197
+ execute_shell(command)
198
+
199
+ Kanrisuru::Result.new(command)
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kanrisuru
4
+ module Core
5
+ module User
6
+ extend OsPackage::Define
7
+
8
+ os_define :linux, :user?
9
+ os_define :linux, :get_uid
10
+ os_define :linux, :get_user
11
+ os_define :linux, :create_user
12
+ os_define :linux, :update_user
13
+ os_define :linux, :delete_user
14
+
15
+ User = Struct.new(:uid, :name, :home, :shell, :groups)
16
+ UserGroup = Struct.new(:gid, :name)
17
+ FilePath = Struct.new(:path)
18
+
19
+ def user?(user)
20
+ result = get_uid(user)
21
+ return false if result.failure?
22
+
23
+ Kanrisuru::Util.present?(result.data) && result.data.instance_of?(Integer)
24
+ end
25
+
26
+ def get_uid(user)
27
+ command = Kanrisuru::Command.new("id -u #{user}")
28
+
29
+ execute(command)
30
+
31
+ Kanrisuru::Result.new(command, &:to_i)
32
+ end
33
+
34
+ def get_user(user)
35
+ command = Kanrisuru::Command.new("id #{user}")
36
+ execute_shell(command)
37
+
38
+ Kanrisuru::Result.new(command) do |cmd|
39
+ ## Get user id
40
+ result = get_uid(user)
41
+ break if result.failure?
42
+
43
+ uid = result.to_i
44
+
45
+ ## Get all groups for the user, with gid and group name
46
+ string = cmd.to_s
47
+ string = string.split('groups=')[1]
48
+ array = string.gsub(/\(/, '').gsub(/\)/, '').split(',')
49
+
50
+ groups = array.map do |str|
51
+ gid = str.delete('^0-9').to_i
52
+ name = str.delete('0-9')
53
+
54
+ UserGroup.new(gid, name)
55
+ end
56
+
57
+ ## Get home / shell path information
58
+ cmd = Kanrisuru::Command.new("getent passwd #{user}")
59
+ cmd | "awk -F: '{print $6, $7}'"
60
+
61
+ execute(cmd)
62
+
63
+ result = Kanrisuru::Result.new(cmd) do |cmd2|
64
+ cmd2.to_s.split.map { |value| FilePath.new(value) }
65
+ end
66
+
67
+ ## TODO: Raise custom error to change parent result to use nested error and mark
68
+ ## as failure.
69
+ break if result.failure?
70
+
71
+ home = result[0]
72
+ shell = result[1]
73
+
74
+ User.new(uid, user, home, shell, groups)
75
+ end
76
+ end
77
+
78
+ def create_user(user, opts = {})
79
+ uid = opts[:uid]
80
+ group = opts[:group]
81
+ groups = opts[:groups]
82
+ home = opts[:home]
83
+ shell = opts[:shell] || '/bin/false'
84
+ createhome = opts[:createhome]
85
+ system_opt = opts[:system]
86
+ skeleton = opts[:skeleton]
87
+ non_unique = opts[:non_unique]
88
+ password = opts[:password]
89
+ expires = opts[:expires] ## YYYY-MM-DD
90
+
91
+ command = Kanrisuru::Command.new("useradd #{user}")
92
+
93
+ if Kanrisuru::Util.present?(uid)
94
+ command.append_arg('-u', uid)
95
+ command.append_flag('-o', non_unique)
96
+ end
97
+
98
+ command.append_flag('-r', system_opt)
99
+ command.append_arg('-s', shell)
100
+ command.append_arg('-d', home)
101
+
102
+ case createhome
103
+ when true
104
+ command.append_flag('-m')
105
+ command.append_arg('-k', skeleton)
106
+ when false
107
+ command.append_flag('-M')
108
+ end
109
+
110
+ if Kanrisuru::Util.present?(group) && group?(group)
111
+ command.append_arg('-g', group)
112
+ elsif group?(user)
113
+ command.append_flag('-N')
114
+ end
115
+
116
+ command.append_arg('-G', groups.join(',')) if Kanrisuru::Util.present?(groups)
117
+
118
+ command.append_arg('-p', password)
119
+ command.append_arg('-e', expires)
120
+
121
+ execute_shell(command)
122
+
123
+ Kanrisuru::Result.new(command) do
124
+ get_user(user).data
125
+ end
126
+ end
127
+
128
+ def update_user(user, opts = {})
129
+ uid = opts[:uid]
130
+ group = opts[:group]
131
+ groups = opts[:groups]
132
+ append = opts[:append]
133
+ home = opts[:home]
134
+ move_home = opts[:move_home]
135
+ shell = opts[:shell] || '/bin/false'
136
+ non_unique = opts[:non_unique]
137
+ password = opts[:password]
138
+ expires = opts[:expires] ## YYYY-MM-DD
139
+ locked = opts[:locked]
140
+
141
+ command = Kanrisuru::Command.new("usermod #{user}")
142
+
143
+ if Kanrisuru::Util.present?(home)
144
+ command.append_arg('-d', home)
145
+ command.append_flag('-m', move_home)
146
+ end
147
+
148
+ command.append_arg('-s', shell)
149
+
150
+ if Kanrisuru::Util.present?(uid)
151
+ command.append_arg('-u', uid)
152
+ command.append_flag('-o', non_unique)
153
+ end
154
+
155
+ command.append_arg('-g', group) if Kanrisuru::Util.present?(group) && group_exists?(group)
156
+
157
+ if Kanrisuru::Util.present?(groups)
158
+ command.append_arg('-G', groups.join(','))
159
+ command.append_flag('-a', append)
160
+ end
161
+
162
+ case locked
163
+ when true
164
+ command.append_flag('-L')
165
+ command.append_arg('-e', 1)
166
+ when false
167
+ command.append_arg('-U')
168
+ command.append_arg('-e', 99_999)
169
+ else
170
+ ## Ensure expires isn't added twice.
171
+ command.append_arg('-e', expires)
172
+
173
+ ## Can't use password with lock / unlock flag.
174
+ command.append_arg('-p', password)
175
+ end
176
+
177
+ execute_shell(command)
178
+
179
+ Kanrisuru::Result.new(command) do |_command|
180
+ get_user(user).data
181
+ end
182
+ end
183
+
184
+ def delete_user(user, opts = {})
185
+ force = opts[:force]
186
+
187
+ return false unless get_uid(user)
188
+
189
+ command = Kanrisuru::Command.new("userdel #{user}")
190
+ command.append_flag('-f', force)
191
+
192
+ execute_shell(command)
193
+
194
+ Kanrisuru::Result.new(command)
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Kanrisuru
6
+ class Logger < ::Logger
7
+ end
8
+ end
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kanrisuru
4
+ class Mode
5
+ class Permission
6
+ attr_reader :symbolic
7
+
8
+ def initialize(numeric, symbolic)
9
+ @numeric = numeric
10
+ @symbolic = symbolic
11
+
12
+ update_symbolic_rwx
13
+ end
14
+
15
+ def to_i
16
+ numeric.to_i
17
+ end
18
+
19
+ def to_s
20
+ symbolic
21
+ end
22
+
23
+ def numeric
24
+ @numeric.to_s
25
+ end
26
+
27
+ def all?
28
+ read? && write? && execute?
29
+ end
30
+
31
+ def symbolic=(symbolic)
32
+ @symbolic = symbolic
33
+
34
+ update_symbolic_rwx
35
+ update_numeric
36
+ end
37
+
38
+ def numeric=(numeric)
39
+ @numeric = numeric
40
+
41
+ update_numeric_rwx
42
+ update_symbolic
43
+ end
44
+
45
+ def read=(boolean)
46
+ @readable = boolean
47
+
48
+ update_numeric
49
+ update_symbolic
50
+ end
51
+
52
+ def write=(boolean)
53
+ @writeable = boolean
54
+
55
+ update_numeric
56
+ update_symbolic
57
+ end
58
+
59
+ def execute=(boolean)
60
+ @executable = boolean
61
+
62
+ update_numeric
63
+ update_symbolic
64
+ end
65
+
66
+ def read?
67
+ @readable
68
+ end
69
+
70
+ def write?
71
+ @writeable
72
+ end
73
+
74
+ def execute?
75
+ @executable
76
+ end
77
+
78
+ private
79
+
80
+ def update_symbolic_rwx
81
+ @readable = @symbolic.include?('r')
82
+ @writeable = @symbolic.include?('w')
83
+ @executable = @symbolic.include?('x')
84
+ end
85
+
86
+ def update_numeric_rwx
87
+ mode = @numeric.to_i(8)
88
+
89
+ @readable = ((mode >> 2) & 0b001) == 1
90
+ @writeable = ((mode >> 1) & 0b001) == 1
91
+ @executable = ((mode >> 0) & 0b001) == 1
92
+ end
93
+
94
+ def update_numeric
95
+ @numeric = (((read? ? 1 : 0) << 2) + ((write? ? 1 : 0) << 1) + (execute? ? 1 : 0)).to_s
96
+ end
97
+
98
+ def update_symbolic
99
+ @symbolic = "#{read? ? 'r' : '-'}#{write? ? 'w' : '-'}#{execute? ? 'x' : '-'}"
100
+ end
101
+ end
102
+
103
+ attr_reader :owner, :group, :other
104
+
105
+ def initialize(mode)
106
+ if mode.instance_of?(Integer)
107
+ init_mode(mode.to_s(8))
108
+ elsif mode.instance_of?(String)
109
+ init_mode(mode)
110
+ else
111
+ raise 'Invalid mode type'
112
+ end
113
+ end
114
+
115
+ def directory?
116
+ @directory == 'd'
117
+ end
118
+
119
+ def symbolic
120
+ @directory + @owner.symbolic + @group.symbolic + @other.symbolic
121
+ end
122
+
123
+ def symbolic=(string)
124
+ raise ArgumentErorr, 'Invalid symbolic type' if string.class != String
125
+
126
+ if string.length == 10 && !string.include?(',')
127
+ modes = string[1..-1]
128
+
129
+ @owner.symbolic = modes[0..2]
130
+ @group.symbolic = modes[3..5]
131
+ @other.symbolic = modes[6..8]
132
+ else
133
+ operations = string.split(',')
134
+ operations.each do |operation|
135
+ flags = operation.split(/[=+-]/)
136
+
137
+ commands = flags[0] == '' ? 'a' : flags[0]
138
+ fields = flags[1]
139
+
140
+ commands = commands.chars
141
+
142
+ commands.each do |command|
143
+ case command
144
+ when 'a'
145
+ apply_operation(operation, fields, @owner)
146
+ apply_operation(operation, fields, @group)
147
+ apply_operation(operation, fields, @other)
148
+ when 'u'
149
+ apply_operation(operation, fields, @owner)
150
+ when 'g'
151
+ apply_operation(operation, fields, @group)
152
+ when 'o'
153
+ apply_operation(operation, fields, @other)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def numeric
161
+ @owner.numeric + @group.numeric + @other.numeric
162
+ end
163
+
164
+ def numeric=(numeric)
165
+ string =
166
+ if numeric.instance_of?(Integer)
167
+ numeric.to_s(8)
168
+ else
169
+ numeric
170
+ end
171
+
172
+ tokens = string.chars
173
+
174
+ @owner.numeric = tokens[0]
175
+ @group.numeric = tokens[1]
176
+ @other.numeric = tokens[2]
177
+ end
178
+
179
+ def inspect
180
+ format('#<Kanrisuru::Mode:0x%<object_id>s @numeric=%<numeric>s @symbolic=%<symbolic>s>',
181
+ object_id: object_id, numeric: numeric, symbolic: symbolic)
182
+ end
183
+
184
+ def to_s
185
+ symbolic
186
+ end
187
+
188
+ def to_i
189
+ numeric.to_i(8)
190
+ end
191
+
192
+ private
193
+
194
+ def apply_operation(operation, fields, permission)
195
+ if operation.include?('=')
196
+ remaining_fields = 'rwx'
197
+
198
+ fields.chars.each do |field|
199
+ apply_field_to_permission(field, permission, true)
200
+
201
+ ## remove any field that shouldn't be set to false
202
+ remaining_fields = remaining_fields.gsub(field, '')
203
+ end
204
+
205
+ ## Apply false to any remaining fields
206
+ apply_field_to_permission(remaining_fields, permission, false)
207
+ elsif operation.include?('-')
208
+ apply_field_to_permission(fields, permission, false)
209
+ elsif operation.include?('+')
210
+ apply_field_to_permission(fields, permission, true)
211
+ end
212
+ end
213
+
214
+ def apply_field_to_permission(fields, permission, boolean)
215
+ permission.read = boolean if fields.include?('r')
216
+ permission.write = boolean if fields.include?('w')
217
+ permission.execute = boolean if fields.include?('x')
218
+ end
219
+
220
+ def init_mode(string)
221
+ string = parse_acl(string)
222
+
223
+ if string.length == 3
224
+ @directory = '-'
225
+ tokens = string.chars
226
+
227
+ @owner = Kanrisuru::Mode::Permission.new(tokens[0], numeric_to_symbolic(tokens[0].to_i))
228
+ @group = Kanrisuru::Mode::Permission.new(tokens[1], numeric_to_symbolic(tokens[1].to_i))
229
+ @other = Kanrisuru::Mode::Permission.new(tokens[2], numeric_to_symbolic(tokens[2].to_i))
230
+ elsif string.length == 10 && !string.include?(',')
231
+ @directory = string[0]
232
+ modes = string[1..-1]
233
+
234
+ @owner = Kanrisuru::Mode::Permission.new(symbolic_to_numeric(modes[0..2]), modes[0..2])
235
+ @group = Kanrisuru::Mode::Permission.new(symbolic_to_numeric(modes[3..5]), modes[3..5])
236
+ @other = Kanrisuru::Mode::Permission.new(symbolic_to_numeric(modes[6..8]), modes[6..8])
237
+ else
238
+ raise ArgumentErorr, "Invalid format for mode #{string}"
239
+ end
240
+ end
241
+
242
+ def parse_acl(string)
243
+ @acl = string[-1] == '.' ? 'selinux' : 'general'
244
+ string.gsub(/[.+]/, '')
245
+ end
246
+
247
+ def symbolic_to_numeric(ref)
248
+ conversions = {
249
+ '---' => 0,
250
+ '---x' => 1,
251
+ '-w-' => 2,
252
+ '-wx' => 3,
253
+ 'r--' => 4,
254
+ 'r-x' => 5,
255
+ 'rw-' => 6,
256
+ 'rwx' => 7
257
+ }
258
+
259
+ conversions[ref]
260
+ end
261
+
262
+ def numeric_to_symbolic(int)
263
+ conversions = {
264
+ 0 => '---',
265
+ 1 => '--x',
266
+ 2 => '-w-',
267
+ 3 => '-wx',
268
+ 4 => 'r--',
269
+ 5 => 'r-x',
270
+ 6 => 'rw-',
271
+ 7 => 'rwx'
272
+ }
273
+
274
+ conversions[int]
275
+ end
276
+ end
277
+ end