Update Tizen 2.0 SDK source code
[sdk/tools/sdk-build.git] / src / pkg_server / installer.rb
1 =begin
2  
3  installer.rb 
4
5 Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6
7 Contact:
8 Taejun Ha <taejun.ha@samsung.com>
9 Jiil Hyoun <jiil.hyoun@samsung.com>
10 Donghyuk Yang <donghyuk.yang@samsung.com>
11 DongHee Yang <donghee.yang@samsung.com>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
16
17 http://www.apache.org/licenses/LICENSE-2.0
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 Contributors:
26 - S-Core Co., Ltd
27 =end
28
29 $LOAD_PATH.unshift File.dirname(__FILE__)
30 $LOAD_PATH.unshift File.dirname(File.dirname(__FILE__))+"/common"
31 require "packageServerConfig"
32 require "log"
33 require "utils"
34 if Utils.is_windows_like_os( Utils::HOST_OS ) then
35     require "rubygems"
36     require "zip/zip"
37 end
38
39 class FileInstaller
40
41     CONFIG_PATH = "#{PackageServerConfig::CONFIG_ROOT}/client"
42     PACKAGE_INFO_DIR = ".info"
43         PACKAGE_MANIFEST = "pkginfo.manifest"
44
45     def FileInstaller.install(package_name, package_file_path, type, target_path, logger)
46
47         if not File.exist? package_file_path then
48             logger.error "\"#{package_file_path}\" file does not exist."
49             return false
50         end
51
52         case type
53         # install script when binary package
54         when "binary" then
55             uniq_name = Utils.create_uniq_name
56             path = Utils::HOME + "/tmp/#{uniq_name}"
57             # windows has limitation for file path length
58             if Utils.is_windows_like_os( Utils::HOST_OS ) then
59                 drive = Utils::HOME.split("/")[0]
60                 path = "#{drive}/#{uniq_name}"
61             end
62             if not File.exist? path then FileUtils.mkdir_p "#{path}" end
63
64                         if File.directory? path then
65                                 log = "## create temporary dir : #{path}\n"
66                         else
67                                 logger.error "Failed to create temporary dir"
68                                 logger.info "  [path: #{path}]"
69                                 return false 
70                         end
71
72             begin
73                                 logger.info "Installing \"#{package_name}\" package.."
74                                 logger.info "  [file: #{package_file_path}]"                            
75
76                 log = log + "## Extract file : #{package_file_path}\n"
77                                 result = extract_file(package_name, package_file_path, path, target_path, logger)
78                                 if result == "" or result.nil? then     
79                                         write_log(target_path, package_name, log)
80                                         return false 
81                                 else log = log + result end
82
83                 log = log + "## Move files : \"#{path}\" to \"#{target_path}\"\n"
84                                 result = move_dir(package_name, path, target_path, logger)
85                                 if result.nil? then 
86                                         write_log(target_path, package_name, log)
87                                         return false
88                                 else log = log + result end                                             
89
90                 log = log + "## Execute install script\n"
91                 result = execute_install_script(package_name, path, target_path, logger)
92                                 if result.nil? then
93                                         write_log(target_path, package_name, log)
94                                         return false
95                                 else log = log + result end                                             
96                                                                                 
97                 log = log + "## Move remove script\n"
98                 result = move_remove_script(package_name, path, target_path, logger)
99                                 if result.nil? then
100                                         write_log(target_path, package_name, log)
101                                         return false
102                                 else log = log + result end                                             
103
104                 log = log + "## Remove temporary dir : #{path} #####\n"
105                 result = Utils.execute_shell_return("rm -rf #{path}")
106                                 if result.nil? then
107                                         logger.warn "Failed to remove temporary path"
108                                         logger.info "  [path: #{path}]"
109                                 end                                     
110             rescue Interrupt
111                 logger.error "FileInstaller: Interrupted.."
112                 Utils.execute_shell("rm -rf #{path}")
113                 logger.info "Removed #{path}"
114                 raise Interrupt
115             end
116                         write_log(target_path, package_name, log)
117 =begin
118             target_config_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
119             if not File.exist? target_config_path then FileUtils.mkdir_p(target_config_path) end
120             pkg_inst_log = "#{package_name}_inst.log"
121             pkg_inst_log_path = File.join(target_config_path, pkg_inst_log)
122
123             File.open(pkg_inst_log_path, "a+") do |f|
124                 f.puts log
125             end
126 =end
127         when "source" then
128         end
129
130         # need verify
131                 logger.info "Installed \"#{package_name}\" package.. OK"
132                 logger.info "  [path: #{target_path}]"
133         return true;
134         end
135
136         def FileInstaller.write_log(target_path, package_name, log)
137                 target_config_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
138                 if not File.exist? target_config_path then FileUtils.mkdir_p(target_config_path) end
139                 pkg_inst_log = "#{package_name}_inst.log"
140                 pkg_inst_log_path = File.join(target_config_path, pkg_inst_log)
141
142                 File.open(pkg_inst_log_path, "a+") do |f|
143                         f.puts log
144                 end
145         end                     
146
147     def FileInstaller.move_remove_script(package_name, path, target_path, logger)
148         target_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
149         if not File.exist? target_path then FileUtils.mkdir_p(target_path) end
150         script_file_prefix = "#{path}/remove.*"
151         script_file = Dir.glob(script_file_prefix)[0]
152                 log = ""
153
154         if not script_file.nil? then
155                         result = Utils.execute_shell_return("mv #{script_file} #{target_path}")
156                         if result.nil? then
157                                 logger.error "Failed to move a remove script"
158                                 logger.info "  [file: #{script_file}]"
159                                 logger.info "  [from: #{path}]"
160                                 logger.info "  [to: #{target_path}]"
161                                 return nil                                              
162                         else log = result.join("") end
163                         logger.info "Moved remove script file.. OK"
164                         log = log + "[file: #{script_file}]\n"
165                         log = log + "[from: #{path}]\n"
166                         log = log + "[to: #{target_path}]\n"
167                 end
168
169                 return log
170     end 
171
172
173         # Does not verify that the script execution is successful.
174         # Register shortcut should be failed.
175     def FileInstaller.execute_install_script(package_name, path, target_path, logger)
176         script_file_prefix = "#{path}/install.*"
177         script_file = Dir.glob(script_file_prefix)[0]
178         log = ""
179         
180         if not script_file.nil? then
181             logger.info "Execute \"#{script_file}\" file"
182             if Utils.is_windows_like_os( Utils::HOST_OS ) then
183                 cmd = "set INSTALLED_PATH=\"#{target_path}\"& #{script_file}"        
184             else
185                 cmd = "INSTALLED_PATH=\"#{target_path}\" #{script_file}"
186                         end
187                         logger.info "  [cmd: #{cmd}]"
188             log = `#{cmd}`
189                         logger.info "Executed install script file.. OK"
190                         log = log + "[file: #{script_file}]\n"
191                         log = log + "[cmd: #{cmd}]\n"
192                 end
193
194         return log
195     end
196
197         # Does not verify that the script execution is successful.
198         # Removing shortcut should be failed.
199     def FileInstaller.execute_remove_script(package_name, target_path, logger)
200         info_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
201         if not File.directory? info_path then
202                         logger.error "\"#{info_path}\" does not exist."                 
203             return nil 
204         end
205
206         script_file_prefix = "#{info_path}/remove.*"
207         script_file = Dir.glob(script_file_prefix)[0]
208         log = ""
209
210         if not script_file.nil? then
211             logger.info "Execute \"#{script_file}\" file"
212             if Utils.is_windows_like_os( Utils::HOST_OS ) then
213                 cmd = "set INSTALLED_PATH=\"#{target_path}\"& #{script_file}"
214             else
215                 cmd = "INSTALLED_PATH=\"#{target_path}\" #{script_file}"
216             end
217                         logger.info "  [cmd: #{cmd}]"
218             log = `#{cmd}`
219                         logger.info "Executed remote script file.. OK"
220                         log = log + "[file: #{script_file}]\n"
221                         log = log + "[cmd: #{cmd}]\n"
222                 end
223
224                 return log
225     end
226
227     def FileInstaller.remove_pkg_files(package_name, target_path, logger)
228         list_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
229
230         if not File.directory? list_path then
231                         logger.error "\"#{list_path}\" does not exist."                         
232             return false 
233         end
234
235         list_file_name = "#{list_path}/#{package_name}.list"
236         list_file = Dir.glob(list_file_name)[0]
237         directories = []
238
239         if not list_file.nil? then
240             File.open(list_file, "r") do |file|
241                 file.each_line do |f|
242                     f = f.strip
243                     if f.nil? or f.empty? then next end
244                     file_path = File.join(target_path, f)
245                     if File.directory? file_path then
246                         if File.symlink? file_path then 
247                             File.unlink file_path
248                             next
249                         end
250                         entries = Dir.entries(file_path)
251                         if entries.include? "." then entries.delete(".") end
252                         if entries.include? ".." then entries.delete("..") end
253                         if entries.empty? or entries.nil? then
254                             begin
255                                 Dir.rmdir(file_path)
256                             rescue SystemCallError
257                                 logger.warn "\"#{file_path}\" directory is not empty"
258                             end 
259                         else directories.push(file_path) end
260                     elsif File.file? file_path then FileUtils.rm_f(file_path)
261                     elsif File.symlink? file_path then File.unlink file_path 
262                     # if files are already removed by remove script,
263                     else logger.warn "\"#{file_path}\" does not exist" end
264                 end
265             end
266
267             directories.reverse.each do |path|
268                                 if not File.directory? path     then next end
269                 entries = Dir.entries(path)
270                 if entries.include? "." then entries.delete(".") end
271                 if entries.include? ".." then entries.delete("..") end
272                 if entries.empty? or entries.nil? then
273                 begin
274                     Dir.rmdir(path)
275                 rescue SystemCallError
276                     logger.warn "\"#{file_path}\" directory is not empty"
277                 end 
278                 else next end
279            end
280         end
281         Utils.execute_shell("rm -rf #{list_path}")
282         return true
283     end
284
285     def FileInstaller.uninstall(package_name, type, target_path, logger)
286         case type
287         when "binary" then
288             result = execute_remove_script(package_name, target_path, logger)
289                         if result.nil? then return false end                                    
290                         if not remove_pkg_files(package_name, target_path, logger) then return false end
291         when "source" then
292         end
293
294         return true
295     end
296
297     def FileInstaller.move_dir(package_name, source_path, target_path, logger)
298         config_path = File.join(target_path, PACKAGE_INFO_DIR, package_name)
299                 pkginfo_path = File.join(source_path, PACKAGE_MANIFEST)
300                 data_path = File.join(source_path, "data")
301                 log = ""
302
303                 if not File.exist? pkginfo_path then
304                         logger.error "#{PACKAGE_MANIFEST} file does not exist. Check #{source_path}"
305                         return nil 
306                 else FileUtils.cp pkginfo_path, config_path end
307
308                 if File.exist? data_path then
309                         # if os is linux, use cpio. it is faster than cp                
310                 if Utils.is_linux_like_os( Utils::HOST_OS ) then
311                                 absolute_path = `readlink -f #{target_path}`
312                         result = Utils.execute_shell_return("cd #{data_path}; find . -depth | cpio -pldm #{absolute_path}")
313                         else                    
314                         result = Utils.execute_shell_return("cp -r #{data_path}/* #{target_path}")
315                         end
316                         if result.nil? then
317                                 logger.error "Failed to move files"
318                                 logger.info "  [from: #{source_path}]"
319                                 logger.info "  [to:  #{target_path}]"
320                                 return nil
321                         end
322                         logger.info "Moved files.. OK"
323                         log = log + "[from: #{source_path}]\n"
324                         log = log + "[to:  #{target_path}]\n"
325                 else logger.warn "\"data\" directory does not exist." end
326
327                 return log
328     end
329
330     def FileInstaller.extract_file(package_name, package_file_path, path, target_path, logger)
331         dirname = File.dirname(package_file_path)
332         filename = File.basename(package_file_path)
333         ext = File.extname(filename)
334
335         target_config_path = target_path + "/#{PACKAGE_INFO_DIR}/#{package_name}"
336         if not File.exist? target_config_path then FileUtils.mkdir_p(target_config_path) end
337         pkg_file_list = "#{package_name}.list"
338         pkg_file_list_path = File.join(target_config_path, pkg_file_list)
339         temp_pkg_file_list = "temp_file_list"
340         temp_pkg_file_list_path = File.join(target_config_path, "temp_file_list")
341
342         show_file_list_command = nil
343         extrach_file_list_command = nil
344                 log = ""
345
346         case ext
347         when ".zip" then
348             show_file_list_command = "zip -sf #{package_file_path}"
349             extract_file_list_command = "unzip -o \"#{package_file_path}\" -d \"#{path}\""
350         when ".tar" then
351             # path should be unix path if it is used in tar command 
352             _package_file_path = Utils.get_unix_path(package_file_path)
353             _path = Utils.get_unix_path(path)
354             show_file_list_command = "tar -tf #{_package_file_path}"
355             extract_file_list_command = "tar xf \"#{_package_file_path}\" -C \"#{_path}\""
356         else
357             logger.error "\"#{filename}\" is not supported."
358             return nil 
359         end
360
361         system "#{show_file_list_command} > #{temp_pkg_file_list_path}"
362         File.open(pkg_file_list_path, "a+") do |targetfile|
363             File.open(temp_pkg_file_list_path, "r") do |sourcefile|
364                 sourcefile.each_line do |l|
365                     if l.strip.start_with? "data/" then
366                         ml = l.strip[5..-1]
367                         targetfile.puts ml
368                     else next end
369                 end
370             end
371         end
372         File.delete(temp_pkg_file_list_path)
373
374         case ext
375         when ".zip" then
376             if Utils.is_windows_like_os( Utils::HOST_OS ) then
377                 log = unzip_file(package_file_path, path)
378             else
379                                 #result = Utils.execute_shell_return(extract_file_list_command)
380                                 #if result.nil? then log = nil 
381                                 #else log = result.join("") end 
382                                 log = `#{extract_file_list_command}`
383             end
384         when ".tar" then
385             #result = Utils.execute_shell_return(extract_file_list_command)
386                         #if result.nil? then log = nil
387                         #else log = result.join("") end                                                 
388                         log = `#{extract_file_list_command}`
389                 end
390
391                 if log == "" then log = nil end
392                 if log.nil? then
393                         logger.error "Failed to extract \"#{filename}\" file"
394                         logger.info "  [file: #{package_file_path}]"
395                         logger.info "  [from: #{path}]"
396                         logger.info "  [to: #{target_path}]"
397                         logger.info "  [cmd: #{extract_file_list_command}]"
398                         return nil
399                 end                     
400
401         logger.info "Extracted \"#{filename}\" file.. OK"
402                 log = log + "[file: #{package_file_path}]\n"
403                 log = log + "[from: #{path}]\n"
404                 log = log + "[to: #{target_path}]\n"
405                 log = log + "[cmd: #{extract_file_list_command}]\n"
406         return log
407     end
408
409     def FileInstaller.extract_a_file(package_file_path, target_file, path, logger)
410         dirname = File.dirname(package_file_path)
411         filename = File.basename(package_file_path)
412         ext = File.extname(filename)
413         
414         case ext
415         when ".zip" then
416             if not path.nil? then
417                 extract_file_command = "unzip -x #{package_file_path} #{target_file} -d #{path}"
418             else
419                 extract_file_command = "unzip -x #{package_file_path} #{target_file}"
420             end
421         when ".tar" then
422             # path should be unix path if it is used in tar command 
423             _package_file_path = Utils.get_unix_path(package_file_path)
424             _path = Utils.get_unix_path(path)
425             if not path.nil? then
426                 extract_file_command = "tar xf #{_package_file_path} -C #{_path} #{target_file}"
427             else
428                 extract_file_command = "tar xf #{_package_file_path} #{target_file}"
429             end
430         end
431
432         system "#{extract_file_command}"
433         
434         if not path.nil? then
435             target_file_path = File.join(path, target_file)
436         else
437             target_file_path = target_file
438         end
439
440         if File.exist? target_file_path then
441             logger.info "Extracted \"#{target_file}\" file.."
442             return true
443         else
444             logger.warn "Failed to extracted \"#{target_file}\" file.."
445                         logger.info "  [file: #{package_file_path}]"
446                         logger.info "  [path: #{path}]"
447                         logger.info "  [cmd: #{extract_file_command}]"
448             return false
449         end
450     end
451
452     def FileInstaller.unzip_file(zipfile, dest)
453         log = "" 
454         Zip::ZipFile.open(zipfile) do |zip_file|
455             zip_file.each do |f|
456                 f_path = File.join(dest, f.name)
457                 FileUtils.mkdir_p(File.dirname(f_path))
458                 if File.exist?(f_path) then
459                     log = log + "[Warn] Exist file : #{f_path}\n" unless f_path.end_with? "/"
460                 else
461                     zip_file.extract(f, f_path)
462                     if not f_path.end_with? "/" then
463                         log = log + "[info] Extracted file : #{f_path}\n"
464                     end
465                 end
466             end
467         end
468         return log
469     end
470
471     def FileInstaller.unzip_a_file(zipfile, file, dest)
472         Zip::ZipFile.open(zipfile) do |zip_file|
473             zip_file.each do |f|
474                 if f.name.strip == file then
475                     f_path = File.join(dest, f.name)
476                     FileUtils.mkdir_p(File.dirname(f_path))
477                     zip_file.extract(f, f_path) unless File.exist?(f_path)
478                     break
479                 end
480             end
481         end
482     end
483 end