kanrisuru 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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