From c8adc57c38cc535e0dcda979565b7639896e7370 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Sun, 22 Mar 2015 07:08:35 +0100 Subject: [PATCH] add ExecCmd and Tempdir --- bin/pharext | Bin 44265 -> 48777 bytes src/pharext/ExecCmd.php | 134 ++++++++++++++++++++++++++++++++++++++ src/pharext/Installer.php | 111 +++++++++++++++++++++---------- src/pharext/Packager.php | 28 +++++--- src/pharext/Tempdir.php | 18 +++++ 5 files changed, 247 insertions(+), 44 deletions(-) create mode 100644 src/pharext/ExecCmd.php create mode 100644 src/pharext/Tempdir.php diff --git a/bin/pharext b/bin/pharext index 79478198dd95ae29458a0a89bd69ed145bd26e2d..65f4d24c12310395bd647732db313df98912fea3 100755 GIT binary patch delta 3310 zcmb7HYitx%6mE;9?n1H9(w5zAxt*@KTXwev6a$34nPqDC}o^rsp$(Zs}viHVUIiJF*bqKW4|_E9J%Hcju$z2}_o zJCA$j^!zss5B$^c=5U~KY*t;}e7H5RYSu@4=fQ)40lvN;hz;WV?Sl<{z3+CeRl58nV9UNxxA*^jL9Js zV@%jO)p=bf90;~SA=JWcJsgVh^-M^3c_AbSE$)i5{nECsXZo*C8LHzD>70Mc@9XdD z^ZQ62+39H(Nm;p^YHBf}r-_@TB$d;MNvY<*xlN1Uhm)Zt4vG!>eN{bX%5&9GgA%Kv z1m{kYoHRB~dOM=KS=;h5S#I3&+`Q*<0_(?Bn~YkP!QT4)zPzoEt1czsacYlR4xMIa z8`ooqjH2zC3~{HhHcfB`bv@fe$287QN>>4OlcU$-Ij-t@&Y++B2)`|BgHMyKa~U%t zslXF2_8tybf>mZn){?YpI5bM8{61gU&FW5kNJ`;ZSpoT#52ZbJSoIQvi&-JajM}!B zcZqO|IId0A9FL7pPp>19vW~!G0W;lnMA>I5QHD|Uv)^i_B0Fe2>QI-k91?UYcm;cc zQ#~EaaeJp13G_zoh&sBtcQq96sViC@Z?y_ zVdkq0sXUSnIWoePUCl};WckpNWlD^MneiyDBJE{|%d)(tL|O>N8o>)z29}wZkQS7l zTbN-5RH+h~{{&VEmL)r|MODk!VvH=~rhR#Fy)cuEO@)kzxe&2NN2sZdqz%u>Mxc3k zV38nPDjmv@VLkwHRbZIgPU-CUstf^`fShU^+S{_B`)4G-7T#m%&iqVJc2 zD~bM!E|xL_?zjk;-zJGZN22G{yE*Vu_E5!~8G^}3+l|~de7oAe@&65t-XnAx zdl3c!p}XO$2fE;$4b5=xoql+2cN@U&juxKfn)Jcz$2;na@X^cNrLR*5=N-oHuVZ<3 zii+`veUU~fq2sFI;Y_08OWLw2NgDbF_LaeXT~l3jDMna<;ymS8geEcxVyZbxaHe9j z0}p;mHJL37-)&u^CO<42lF9>`MIB;VtWW0hhN`2~D7H4jGiYWAe%rhpHC;z2^q1Dy zq~S38sPts1sv*>fgU48xq9yT(!ZM8l(R2q57KjkzlimcyUCq44B^ip9%7!l0ER@HN z0!|EU9%DbUOA1}lIQg6*{*+wzLId2fe_o?y>ShM!PIj&f8&*m+WV4fvIHF{9H)U8R zwmDurGqofXk-5LBA|o0FygEU^S+xKjITC{H#g@`{6C3JbW3e4}7h-K3FFu61-HxTe zq92LELxtx$YrX^#g-Z!^)`#Hd&CPJjA*H@pG7f#*2P8l!XEGKK>+4ak>^h|wB#}5g38fhShBmXfC*YzqCYt;zHpIr`d{+VTVU VAAP&kK62t){O6@^^9{G$@Gn>Q05t#r delta 1050 zcmah|PiWI%6wg}9Y;m)5Y_4qzX$aCtx^4^#vpRH&TTurd#$cnZe_g-p0J?~$8 z?q$NhPA^66g0b+RH@4IU^>CkUH^RbB?2o5C6m|RYY8P;kSnx|XMUfz}l>_E|7?1SY z)t4fC@L|kJ61&>9HV;oCyq)|Q8L(}PPT6*n?zO4EwNTXh%gwLwoaXGr2U@UgDC+cZ zBI>YLqX`nX?nZAWt>Y8f)^^S;$fd-PisnpI)GcfJkuMYQ`;$pVFX-}lgx_unnnzNc zb)u}mxKZrKSzO2Rd^+F&B~8uA`Lclm@Cv2jNWBA?ToC$msiEq2x~*Ew$o3}xbUD$; zm$pcY;yDC|>OAeVL0kfH?G&4s9bmJdj`pPfQGfx75}U=Oet*^NR#r=TgLgr&<&kSl3rLqdejrUh*@ zYVE$lx9n&nV4)U*&08TjRTJR)wJunxT@AV8*pjxfL>JD~`>h-Gr~6tM=ve85!h<&J k(|Wa)7T$b+`{IuIdH=M}+w-h%a)DW1$3HqXGJJCQ7i+soQ2+n{ diff --git a/src/pharext/ExecCmd.php b/src/pharext/ExecCmd.php new file mode 100644 index 0000000..056f0b2 --- /dev/null +++ b/src/pharext/ExecCmd.php @@ -0,0 +1,134 @@ +command = $command; + $this->verbose = $verbose; + + /* interrupt output stream */ + if ($verbose) { + printf("\n"); + } + } + + /** + * (Re-)set sudo command + * @param string $sudo + */ + public function setSu($sudo = false) { + $this->sudo = $sudo; + } + + /** + * Execute a program with escalated privileges handling interactive password prompt + * @param string $command + * @param string $output + * @param int $status + */ + private function suExec($command, &$output, &$status) { + if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { + $status = -1; + throw new \Exception("Failed to run {$command}"); + } + $stdout = $pipes[1]; + $passwd = 0; + while (!feof($stdout)) { + $R = [$stdout]; $W = []; $E = []; + if (!stream_select($R, $W, $E, null)) { + continue; + } + $data = fread($stdout, 0x1000); + /* only check a few times */ + if ($passwd++ < 10) { + if (stristr($data, "password")) { + printf("\n%s", $data); + } + } + $output .= $data; + } + $status = proc_close($proc); + } + + /** + * Run the command + * @param array $args + * @throws \Exception + */ + public function run(array $args = null) { + $exec = escapeshellcmd($this->command); + if ($args) { + $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); + } + + if ($this->sudo) { + $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->output, $this->status); + } elseif ($this->verbose) { + ob_start(function($s) { + $this->output .= $s; + return $s; + }, 1); + passthru($exec, $this->status); + ob_end_flush(); + } else { + exec($exec ." 2>&1", $output, $this->status); + $this->output = implode("\n", $output); + } + + if ($this->status) { + throw new \Exception("Command {$this->command} failed ({$this->status})"); + } + } + + /** + * Retrieve exit code of cmd run + * @return int + */ + public function getStatus() { + return $status; + } + + /** + * Retrieve output of cmd run + * @return string + */ + public function getOutput() { + return $this->output; + } +} diff --git a/src/pharext/Installer.php b/src/pharext/Installer.php index 5e86f31..b41d7fd 100644 --- a/src/pharext/Installer.php +++ b/src/pharext/Installer.php @@ -69,7 +69,7 @@ class Installer implements Command $phar = new Phar(Phar::running(false)); foreach ($phar as $entry) { if (fnmatch("*.ext.phar*", $entry->getBaseName())) { - $temp = $this->newtemp($entry->getBaseName()); + $temp = new Tempdir($entry->getBaseName()); $phar->extractTo($temp, $entry->getFilename(), true); $phars[$temp] = new Phar($temp."/".$entry->getFilename()); } @@ -140,37 +140,58 @@ class Installer implements Command $this->error(null); exit(4); } - - // phpize - $this->exec("phpize", $this->php("ize")); - - // configure - $args = ["--with-php-config=". $this->php("-config")]; - if ($this->args->configure) { - $args = array_merge($args, $this->args->configure); - } - $this->exec("configure", "./configure", $args); - - // make - if ($this->args->verbose) { - $this->exec("make", "make", ["-j3"]); - } else { - $this->exec("make", "make", ["-j3", "-s"]); - } - - // install - if ($this->args->verbose) { - $this->exec("install", "make", ["install"], true); - } else { - $this->exec("install", "make", ["install", "-s"], true); - } - - // activate + + $this->build(); $this->activate(); - - // cleanup $this->cleanup($temp); } + + /** + * Phpize + trinity + */ + private function build() { + try { + // phpize + $this->info("Runnin phpize ... "); + $cmd = new ExecCmd($this->php("ize"), $this->args->verbose); + $cmd->run(); + $this->info("OK\n"); + + // configure + $this->info("Running configure ... "); + $args = ["--with-php-config=". $this->php("-config")]; + if ($this->args->configure) { + $args = array_merge($args, $this->args->configure); + } + $cmd = new ExecCmd("./configure", $this->args->verbose); + $cmd->run($args); + $this->info("OK\n"); + + // make + $this->info("Running make ... "); + $cmd = new ExecCmd("make", $this->args->verbose); + if ($this->args->verbose) { + $cmd->run(["-j3"]); + } else { + $cmd->run(["-j3", "-s"]); + } + $this->info("OK\n"); + + // install + $this->info("Running make install ... "); + $cmd->setSu($this->args->sudo); + if ($this->args->verbose) { + $cmd->run(["install"]); + } else { + $cmd->run(["install", "-s"]); + } + $this->info("OK\n"); + + } catch (\Exception $e) { + $this->error("%s\n", $e->getMessage()); + $this->error("%s\n", $cmd->getOutput()); + } + } /** * Perform any cleanups @@ -233,13 +254,31 @@ class Installer implements Command $path = $temp->getPathname(); $stat = stat($file); - $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); - $this->exec("INI owner transfer", "chown", [$ugid, $path], true); - - $perm = decoct($stat["mode"] & 0777); - $this->exec("INI permission transfer", "chmod", [$perm, $path], true); - - $this->exec("INI activation", "mv", [$path, $file], true); + try { + $this->info("Running INI owner transfer ... "); + $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); + $cmd = new ExecCmd("chown", $this->args->verbose); + $cmd->setSu($this->args->sudo); + $cmd->run([$ugid, $path]); + $this->info("OK\n"); + + $this->info("Running INI permission transfer ... "); + $perm = decoct($stat["mode"] & 0777); + $cmd = new ExecCmd("chmod", $this->args->verbose); + $cmd->setSu($this->args->sudo); + $cmd->run([$perm, $path]); + $this->info("OK\n"); + + $this->info("Running INI activation ... "); + $cmd = new ExecCmd("mv", $this->args->verbose); + $cmd->setSu($this->args->sudo); + $cmd->run([$path, $file]); + $this->info("OK\n"); + } catch (\Exception $e) { + $this->error("%s\n", $e->getMessage()); + $this->error("%s\n", $cmd->getOutput()); + exit(5); + } } } } diff --git a/src/pharext/Packager.php b/src/pharext/Packager.php index 33aa72e..d66e83b 100644 --- a/src/pharext/Packager.php +++ b/src/pharext/Packager.php @@ -95,6 +95,10 @@ class Packager implements Command } try { + /* source needs to be evaluated before CliArgs validation, + * so e.g. name and version can be overriden and CliArgs + * does not complain about missing arguments + */ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { @@ -115,7 +119,10 @@ class Packager implements Command if ($errs) { if (!$this->args["quiet"]) { - $this->header(); + if (!headers_sent()) { + /* only display header, if we didn't generate any output yet */ + $this->header(); + } } foreach ($errs as $err) { $this->error("%s\n", $err); @@ -154,9 +161,13 @@ class Packager implements Command */ private function download($source) { if ($this->args["git"]) { - $local = $this->newtemp("gitclone"); - $this->exec("git clone", "git", ["clone", $source, $local]); - $source = $local; + $this->info("Cloning %s ... ", $source); + $local = new Tempdir("gitclone"); + $cmd = new ExecCmd("git", $this->args->verbose); + $cmd->run(["clone", $source, $local]); + if (!$this->args->verbose) { + $this->info("OK\n"); + } } else { $this->info("Fetching remote source %s ... ", $source); if (!$remote = fopen($source, "r")) { @@ -169,12 +180,11 @@ class Packager implements Command exit(2); } $local->closeStream(); - $source = $local->getPathname(); $this->info("OK\n"); } $this->cleanup[] = $local; - return $source; + return $local->getPathname(); } /** @@ -183,8 +193,10 @@ class Packager implements Command * @return string extracted directory */ private function extract($source) { - $dest = $this->newtemp("local"); - $this->info("Extracting to %s ... ", $dest); + $dest = new Tempdir("local"); + if ($this->args->verbose) { + $this->info("Extracting to %s ... ", $dest); + } $archive = new PharData($source); $archive->extractTo($dest); $this->info("OK\n"); diff --git a/src/pharext/Tempdir.php b/src/pharext/Tempdir.php new file mode 100644 index 0000000..585033a --- /dev/null +++ b/src/pharext/Tempdir.php @@ -0,0 +1,18 @@ +