Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
6.9 KiB

  1. package PPM::InstallerClient;
  2. use strict;
  3. use Socket;
  4. use Cwd qw(getcwd);
  5. use File::Basename qw(dirname basename);
  6. use File::Path qw(mkpath rmtree);
  7. use constant FIELD_SEP => "\001";
  8. use constant FIELD_UNDEF => "\002";
  9. use Data::Dumper;
  10. $PPM::InstallerClient::VERSION = '3.0';
  11. #=============================================================================
  12. # API:
  13. #=============================================================================
  14. sub init {
  15. my ($ppm_port, $inst) = @_;
  16. my ($paddr, $proto, $msg);
  17. # Set up a temporary socket server and waits for the frontend to connect
  18. # to it.
  19. # TODO: put this in a big while(1) loop, and keep a list of connected
  20. # frontends. That way, we can service multiple front-ends at once, which
  21. # prevents multiple instances of the same target from clobbering each
  22. # other's changes.
  23. $proto = getprotobyname('tcp');
  24. socket(SERVER, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
  25. setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR,
  26. pack('l', 1)) || die "setsockopt: $!";
  27. bind(SERVER, sockaddr_in($ppm_port, INADDR_ANY)) || die "bind: $!";
  28. listen(SERVER, SOMAXCONN);
  29. $paddr = accept(CLIENT, SERVER);
  30. my ($port, $iaddr) = sockaddr_in($paddr);
  31. my $name = gethostbyaddr($iaddr, AF_INET);
  32. select((select(CLIENT), $| = 1)[0]);
  33. my $fd = \*CLIENT;
  34. close(SERVER);
  35. my %tmpdirs;
  36. my $cwd = getcwd;
  37. # Read commands from the socket:
  38. while ($msg = recvmsg($fd)) {
  39. # To do:
  40. # 1. Decide what action is being requested;
  41. # 2. Parse the "packet" into the required arguments;
  42. # 3. Invoke the action callback on the $inst interface; and
  43. # 4. Respond over the socket.
  44. my ($cmd, @args) = decode_record($msg);
  45. # Package Operations
  46. if ($cmd eq 'QUERY') {
  47. my @ppds = $inst->query(@args);
  48. my @records = map { encode_record($_) } @ppds;
  49. local $" = "\n";
  50. sendmsg($fd, "@records");
  51. }
  52. elsif ($cmd eq 'PROPERTIES') {
  53. my @fields = $inst->properties(@args);
  54. if (@fields) {
  55. sendmsg($fd, encode_record(@fields));
  56. }
  57. else {
  58. sendmsg($fd, "NOK");
  59. }
  60. }
  61. elsif ($cmd eq 'REMOVE') {
  62. my $ret = $inst->remove(@args);
  63. if ($ret) {
  64. sendmsg($fd, "OK");
  65. }
  66. else {
  67. sendmsg($fd, "NOK");
  68. }
  69. }
  70. elsif ($cmd eq 'PRECIOUS') {
  71. my @ret = $inst->precious();
  72. sendmsg($fd, encode_record(@ret));
  73. }
  74. elsif ($cmd eq 'BUNDLED') {
  75. my @ret = $inst->bundled();
  76. sendmsg($fd, encode_record(@ret));
  77. }
  78. elsif ($cmd eq 'DEPENDENTS') {
  79. my @ret = $inst->dependents(@args);
  80. if (@ret == 1 and not defined $ret[0]) {
  81. sendmsg($fd, 'NOK');
  82. }
  83. elsif (@ret == 0) {
  84. sendmsg($fd, encode_record(undef));
  85. }
  86. else {
  87. sendmsg($fd, encode_record(@ret));
  88. }
  89. }
  90. # Configuration Operations
  91. elsif ($cmd eq 'CONFIG_INFO') {
  92. my @ret = $inst->config_info;
  93. if (@ret) {
  94. my @records = map { encode_record(@$_) } @ret;
  95. local $" = "\n";
  96. sendmsg($fd, "@records");
  97. }
  98. else {
  99. sendmsg($fd, "NOK");
  100. }
  101. }
  102. elsif ($cmd eq 'CONFIG_KEYS') {
  103. my @ret = $inst->config_keys;
  104. if (@ret) {
  105. my @records = map { encode_record(@$_) } @ret;
  106. local $" = "\n";
  107. sendmsg($fd, "@records");
  108. }
  109. else {
  110. sendmsg($fd, "NOK");
  111. }
  112. }
  113. elsif ($cmd eq 'CONFIG_GET') {
  114. my $ret = $inst->config_get(@args);
  115. if ($ret) {
  116. sendmsg($fd, $ret);
  117. }
  118. else {
  119. sendmsg($fd, "NOK");
  120. }
  121. }
  122. elsif ($cmd eq 'CONFIG_SET') {
  123. if ($inst->config_set(@args)) {
  124. sendmsg($fd, "OK");
  125. }
  126. else {
  127. sendmsg($fd, "NOK");
  128. }
  129. }
  130. elsif ($cmd eq 'ERROR_STR') {
  131. sendmsg($fd, $inst->error_str);
  132. }
  133. # Install and remove: the installerlib must substitute its own notion
  134. # of the tempdir if it knows it exists:
  135. elsif ($cmd eq 'INSTALL') {
  136. # The following line is for reference:
  137. # my ($pkg, $ppmpath, $ppd, $repos, $ppmpath) = @args;
  138. $args[1] = $tmpdirs{$args[0]} if exists $tmpdirs{$args[0]};
  139. my $ret = $inst->install(@args);
  140. if ($ret) {
  141. sendmsg($fd, "OK");
  142. }
  143. else {
  144. sendmsg($fd, "NOK");
  145. }
  146. }
  147. elsif ($cmd eq 'UPGRADE') {
  148. # The following line is for reference:
  149. # my ($pkg, $ppmpath, $ppd, $repos, $ppmpath) = @args;
  150. $args[1] = $tmpdirs{$args[0]} if exists $tmpdirs{$args[0]};
  151. my $ret = $inst->upgrade(@args);
  152. if ($ret) {
  153. sendmsg($fd, "OK");
  154. }
  155. else {
  156. sendmsg($fd, "NOK");
  157. }
  158. }
  159. # Transmission of files via the network
  160. elsif ($cmd eq 'PKGINIT') {
  161. my $pkg = shift @args;
  162. my $tmpdir = $inst->config_get("tempdir");
  163. unless ($tmpdir and -w $tmpdir) {
  164. sendmsg($fd, encode_record('NOK', "Backend tempdir '$tmpdir' not writeable"));
  165. next;
  166. }
  167. $tmpdir .= "/$pkg-$$";
  168. mkpath($tmpdir);
  169. $tmpdirs{$pkg} = $tmpdir;
  170. sendmsg($fd, 'OK');
  171. }
  172. elsif ($cmd eq 'PKGFINI') {
  173. my $pkg = shift @args;
  174. my $path = $tmpdirs{$pkg} or do {
  175. sendmsg($fd,
  176. encode_record('NOK', 'pkgfini() without pkginit()'));
  177. next;
  178. };
  179. rmtree($path);
  180. delete $tmpdirs{$pkg};
  181. sendmsg($fd, 'OK');
  182. }
  183. elsif ($cmd eq 'TRANSMIT') {
  184. my $pkg = shift @args;
  185. my $tmpdir = $tmpdirs{$pkg};
  186. my $file = shift @args;
  187. my $dir = dirname($file);
  188. chdir($tmpdir);
  189. mkpath($dir);
  190. eval {
  191. open(FILE, "> $file") || die "can't write $file: $!";
  192. binmode(FILE) || die "can't binmode $file: $!";
  193. };
  194. if ($@) {
  195. sendmsg($fd, encode_record('NOK', "$@"));
  196. next;
  197. }
  198. sendmsg($fd, 'OK');
  199. my $msg;
  200. while ($msg = recvmsg($fd)) {
  201. my ($flag, $data) = decode_record($msg);
  202. last if $flag eq 'EOT';
  203. print FILE $data;
  204. }
  205. eval {
  206. close(FILE) || die "can't close $file: $!";
  207. };
  208. if ($@) {
  209. sendmsg($fd, encode_record('NOK', "$@"));
  210. next;
  211. }
  212. sendmsg($fd, 'OK');
  213. chdir($cwd);
  214. }
  215. elsif ($cmd eq 'STOP') {
  216. close(CLIENT);
  217. last;
  218. }
  219. else {
  220. die "Unrecognized command: $cmd";
  221. }
  222. }
  223. }
  224. #=============================================================================
  225. # Private functions!
  226. #=============================================================================
  227. my $EOL = "\015\012";
  228. sub sendmsg {
  229. my $fd = shift;
  230. my $msg = shift;
  231. local $\ = "$EOL.$EOL";
  232. print $fd $msg;
  233. }
  234. sub recvmsg {
  235. my $fd = shift;
  236. local $/ = "$EOL.$EOL";
  237. my $msg = <$fd>;
  238. chomp $msg if $msg;
  239. return $msg;
  240. }
  241. sub qmeta {
  242. local $_ = shift || $_;
  243. s{([^A-Za-z0-9])}{sprintf('\x%.2X',ord($1))}eg;
  244. $_;
  245. }
  246. sub uqmeta {
  247. local $_ = shift || $_;
  248. eval qq{qq{$_}};
  249. }
  250. sub encode_record {
  251. my @fields = map { my $a = defined $_ ? $_ : FIELD_UNDEF; qmeta($a) } @_;
  252. join FIELD_SEP, @fields;
  253. }
  254. sub decode_record {
  255. my $t = shift || $_;
  256. return map { $_ = &uqmeta; $_ = undef if $_ eq FIELD_UNDEF; $_ }
  257. split(FIELD_SEP, $t, -1);
  258. }