#!/usr/bin/env ruby require 'zfs' require 'socket' require 'json' require 'uuid' require_relative 'zfs_monkeypatch' load File.dirname($0) + "/" + 'config.rb' puts "Filesystem base: #{Config[:basefs]}" puts "Default user quota: #{Config[:default_user_quota]}" puts "Socket path: #{Config[:socketpath]}" sockdir = File.dirname(Config[:socketpath]) puts "Socket directory: #{sockdir}" # Check if socket directory exists, if not - create if with apropriate options Dir.mkdir(sockdir, 0700) if not File.exists?(sockdir) File.umask(0077) puts "Setting up UUID generator" UUID.state_file = false puts "Starting socket server" server = UNIXServer.open(Config[:socketpath]) loop do Thread.start(server.accept) do |client| begin puts "Accepted new client connection" uuid = UUID.new puts "Sending our greeting" client.puts({:status => 0, :content => {:app => "zvolcreator", :apiversion => 0}}.to_json) response = JSON.parse(client.gets) puts "Client response: #{response}" raise ArgumentError.new("Bad status") if response["status"] != 0 puts "Sending basic server info" client.puts({:status => 0, :content => {:basefs => Config[:basefs], :default_user_quota => Config[:default_user_quota]}}.to_json) response = JSON.parse(client.gets) puts "Client response: #{response}" raise ArgumentError.new("Bad status") if response["status"] != 0 raise ArgumentError.new("No user specified") if not response["content"]["user"] raise ArgumentError.new("Volume size not specified") if not response["content"]["volsize"] # poor man's check for nasty characters in usernames raise ArgumentError.new("Invalid characters in username") if response["content"]["user"] =~ /[\/\\ ]/ userfs = Config[:basefs] + "/" + response["content"]["user"].to_s ZFS(userfs).create(:zfsopts => ["quota=#{Config[:default_user_quota]}"]) volpath = userfs + "/" + uuid.generate ZFS(volpath).create(:sparse => true, :volume => response["content"]["volsize"]) client.puts({:status => 0, :content => {:volpath => volpath}}.to_json) client.close rescue JSON::ParserError, ArgumentError => e puts "Error: #{e.to_s}" client.puts({:status => 1, :content => e.to_s}) client.close Thread.kill end end end