2014-10-28

Stop itamae-1.0.4 from using sudo

UPDATE 2014-11-07

My pull request has been merged.



I'm using itamae for provisioning some servers. However, I don't have sudo access. Here's how I stopped itamae from using sudo.

diff --git a/lib/itamae/backend.rb b/lib/itamae/backend.rb
index d4f6e35..25bab62 100644
--- a/lib/itamae/backend.rb
+++ b/lib/itamae/backend.rb
@@ -33,6 +33,7 @@ module Itamae
       when :ssh
         Specinfra.configuration.request_pty = true
         Specinfra.configuration.host = options.delete(:host)
+        Specinfra.configuration.disable_sudo = options.delete(:disable_sudo)
         Specinfra.configuration.ssh_options = options

         Specinfra.configuration.backend = :ssh
diff --git a/lib/itamae/cli.rb b/lib/itamae/cli.rb
index 8c40fe9..544a0e0 100644
--- a/lib/itamae/cli.rb
+++ b/lib/itamae/cli.rb
@@ -35,6 +35,7 @@ module Itamae
     option :ohai, type: :boolean, default: false
     option :vagrant, type: :boolean, default: false
     option :ask_password, type: :boolean, default: false
+    option :sudo, type: :boolean, default: true
     def ssh(*recipe_files)
       if recipe_files.empty?
         raise "Please specify recipe files."
diff --git a/lib/itamae/runner.rb b/lib/itamae/runner.rb
index 37908fc..3a5c96d 100644
--- a/lib/itamae/runner.rb
+++ b/lib/itamae/runner.rb
@@ -54,6 +54,7 @@ module Itamae
           opts[:user] = options[:user] || Etc.getlogin
           opts[:keys] = [options[:key]] if options[:key]
           opts[:port] = options[:port] if options[:port]
+          opts[:disable_sudo] = true unless options[:sudo]

           if options[:vagrant]
             config = Tempfile.new('', Dir.tmpdir)

This can be applied to a bundled itamae as follows.

#!/bin/bash

DIR=$(dirname "$0")

bundle install --path "$DIR/vendor/bundle"

# itamaeのno sudoパッチを当てる
pushd vendor/bundle/ruby/2.1.0/gems/itamae-1.0.4/lib/itamae &>/dev/null
patch <../../../../../../../../itamae-1.0.4-no-sudo.patch
popd &>/dev/null

Then, to use it...  (I couldn't get --no-sudo to work)

bundle exec itamae ssh -h server -u user --sudo=false recipes/recipe.rb

How to watch for correct input in bash

I've seen some pretty crazy ways to do this. Here's how I do it, which I think is the simplest way.
#!/bin/bash

while true; do
    read -p 'Prompt? (y/n) ' yn
    case "$yn" in
        y|Y)
            echo 'YES!'
            break;
            ;;
        n|N)
            echo 'NO!'
            break;
            ;;
    esac
done
This will loop until one of [yYnN] is entered.

2014-03-25

How to Change Atom's `navigator.language`

After tinkering a bit, I've finally figured out how to change the navigator.language! I ended up having to modify the libchromiumcontent.dylib bundled inside Atom.app...
$ cmp -bl {orig,ja-JP}/Atom.app/Contents/Frameworks/Atom\ Framework.framework/Versions/A/Libraries/libchromiumcontent.dylib
43700712 145 e 152 j
43700713 156 n 141 a
43700715 125 U 112 J
43700716 123 S 120 P
This is the value that is used for navigator.language. Once patched as above, navigator.language will return "ja-JP".

2014-03-21

Block Cursor for Atom

Atom's cursor doesn't change to cover the character behind it.  So, if you work with CJK files and want a block cursor (à la vim-mode), then you're left with a half-character-width cursor (the cursor width is set to one &nbsp; by default).

I made a patch for Atom v0.75.0 to fix this issue.

  • CJK Character
    • Before:
    • After:
  • Tab
    • Before:
    • After:

Atom File Types

There wasn't a straight-forward way to associate an arbitrary file type (file extension) with a grammar in Atom.  So, I made a package to do just that.

https://atom.io/packages/file-types

2012-10-04

The Node.js REPL and Backslashes in Strings

I thought there was something weird going on with the node.js REPL's handling of backslashes in Strings. I was thinking, "Why are there twice as many backslashes!?" Then, it hit me: the output is correct! It is escaping the value of the string, just as if a programmer were to have typed it. Duh. You can use the output as-is when copy-and-pasting. No need to manually add extra backspaces! That's pretty convenient, eh? This really had me confused me at first, though, because I am so used to the behavior of Chrome's console, which prints out the unescaped string values (even though it does put quotes around the value).

$ node
> "\ "
' '
> JSON.stringify("\ ")
'" "'
> "\\"
'\\'
> JSON.stringify("\\")
'"\\\\"'
> new Buffer("\\")
<Buffer 5c>
> new Buffer([0x5c]).toString()
'\\'
> new Buffer([0x5c, 0x5c]).toString()
'\\\\'

2012-02-29

U+2665 BLACK HEART SUIT

I was reading an article on StackOverflow when one of the ads caught my eye. It looked something like this:

1
2
3
4 <p>
5 &#x2665;
6 Your Job
7 </p>
8
9

"What character is that?" I wondered whilst automatically opening up the in-browser console.

> String.fromCharCode(0x2665)
""

Hey, it's the U+2665 BLACK HEART SUIT! So, that would make the message: ♥ Your Job.

Cool.

2012-01-06

Tomcat v6.0.35 and UTF-8 Parameters

Update 1 (2012-01-07): I don't have access to the problematic system right now and am unable to confirm; but, when I tried simplifying this down to just one JSP, it worked fine. It also worked fine with one mayaa file. This leads me to think that there must be some system-specific issue.

The recent release of Apache Tomcat, v6.0.35, seems to break the handling of parameters encoded in UTF-8. For example, if I pass "%E6%97%A5%E6%9C%AC" (which is the string of URL-escaped UTF-8 bytes for "日本"), it gets incorrectly interpreted. Both URIEncoding="UTF-8" and useBodyEncodingForURI="true" are set for the necessary Connectors in server.xml, and it works as expected prior to v6.0.35.

Expected:
$ cat nippon && cat $_ | hexdump -C
日本
00000000  e6 97 a5 e6 9c ac 0a                              |.......|
00000007

Actual:
$ cat tomcat-bug && cat $_ | hexdump -C
æ¥æ¬
00000000  c3 a6 c2 97 c2 a5 c3 a6  c2 9c c2 ac 0a           |.............|
0000000d

I cloned the GitHub mirror of tomcat60 and did a quick git-bisect. The offending commit is 1ef4156 (r1200601 in SVN), which corresponds to the last two items of the Catalina changelog for unreleased version 6.0.34.

So, in other words, Tomcat properly interprets parameters prior to (and fails starting from) 1ef4156.

It is hard to tell exactly what the problem is, though, because 1ef4156 is such a large commit. My best guess, without digging into the code, is that ISO-8859-1 is being used instead of UTF-8 in the decoding process—i.e., it seems that the charset is not being correctly passed to the parameter processor.

The same "mistaken" decoding can be done with iconv, as follows:
$ cat nippon | iconv -f ISO-8859-1 -t UTF-8 | hexdump -C
00000000  c3 a6 c2 97 c2 a5 c3 a6  c2 9c c2 ac 0a           |.............|
0000000d

Maybe I'll have a look later and try to fix the problem.

2011-10-08

Different fetch/push URLs for git clone

UPDATE 1
It turns out to be reeeeallly easy!
git remote set-url --push origin git@example.com:username/repo.git


I always use passphrases on my ssh keys. Everyone does, right? Anyway, it gets kind of annoying entering it in each time when fetching from an upstream repository. I don't have enough need to set-up a key-ring, though. So, I took a quick look at git config --help and figured out how to set-up different URLs for git-push and git-fetch.
remote.<name>.url
    The URL of a remote repository. See git-fetch(1) or git-push(1).
remote.<name>.pushurl
    The push URL of a remote repository. See git-push(1).

I just opened up .git/config and set remote.origin.url to the read-only URL, and remote.origin.pushurl to the initial value of remote.origin.url.

Now, I can git-fetch/git-pull without the annoying passphrase prompt. Yay.

There's probably a way to do it with git-remote; but, I'm too lazy to figure it out right now...

2010-07-21

Cygwin.bat

I use Cygwin in combination with Poderosa for my terminal emulator (because it has tabs, unlike PuTTY), as a replacement for the Win32 console. These two are usually the first things that I install on a new Windows box.

I recently changed the drive letter of the drive to which Cygwin (v1.7.5) had been installed. After the change, Cygwin just ceased to start-up at all. Even after running setup again.

After some digging, I finally figured out that path\to\cygwin\Cygwin.bat contains an absolute reference to Cygwin's bin directory. However, it seems that this path only gets set once, at initial installation.

After updating the drive letter manually, though, everything is back to normal. I think the setup should detect and correct issues with this batch file...

@echo off

E:
chdir E:\cygwin\bin

bash --login -i

2010-01-20

Installing RMagick

The other day I had to install RMagick in order to get a RoR project running on a local box to do some maintenance and add new functionality.

Well, I thought everything would be all fine and dandy because I had installed ImageMagick to mess around with previously. But, for some reason, gem install rmagick was complaining it couldn't find Magick-Config or something.

I ended up trying a bunch of suggestions from other blogs (which I conveniently forgot to list...sorry for not giving credits) and somehow came up with this configure for ImageMagick:
./configure --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8

I've not had the time nor the patience to figure out which option is the key to this all; but, RMagick installed without problem.

2009-12-30

Closure Compiler

I've been trying out Google's supposedly wicked Closure Compiler. It seems to work well for normal things; but, there still is a bit farther to go. I don't know if there is something wrong with the way that I write javascript; but, it seems that the Closure Compiler just can't handle it properly...

By the way, there was a very eye-opening and interesting post on the Google Code Blog. Basically, they came up with a wonderful idea: send all modules, split-up and commented-out, to the client, then eval() them on-the-fly. Now, why the hell didn't anyone think of that before!? D'OH!!! Anyway, props to the thinker who came up with that solution.

Well, okay, back to the point of this blog.

In order to see how well the compiler works, I wrote some slightly bloated code (see code appendices below).

For some reason, though, the output is really not as optimized as I would have hoped...

For example:
if((b=b)&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}a=(a=a)?a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"):void 0;if(a){a=["{",a,"}"].join("");eval(a)}

Could probably be further reduced to:
if(b&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}if(a){eval(["{",a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"),"}"].join(""))}

And even further yet to:
if(b&&f===b.tagName){eval(["{",(b.innerHTML||"").replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"),"}"].join(""));b.parentNode.removeChild(b)}

Maybe there's some cross-browser stuff (of which I should mention I am not really familiar). I dunno.

All-in-all, from what I've seen so far, it's really good at reducing unnecessary, or "dead", code. As such, I'd have say that I want to start using the Closure Compiler for my own projects! At this point it looks like nothing beats a hand-coder for optimization, though--I'll have to go over the output and manually fix all the (a = a) stuff.





Appendix


module.js
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// @formatting pretty_print
// ==/ClosureCompiler==

// ADD YOUR CODE HERE
var xvn = function(doc){
 var SCRIPT = doc.createElement('script').tagName;

 var __loaded_modules = {};
 function mark_as_loaded(id) {
  __loaded_modules[id] = true;
 }
 function is_loaded(id) {
  return (id) && (__loaded_modules[id]);
 }

 function get_elm(id) {
  var elm;
  if (id) {
   elm = doc.getElementById(id);
  }
  return elm;
 }

 function grab_js(elm) {
  var js;
  if (elm && SCRIPT === elm.tagName) {
   js = elm.innerHTML || "";
   elm.parentNode.removeChild(elm);
  }
  return js;
 }

 function clean_js(js) {
  if (!js) return;

  return js.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1");
 }

 function eval_js(js) {
  if (!js) return;

  var new_js = ['{', js, '}'].join("");
  eval(new_js);
 }

 return {
  'load_module':function(id){
   if (!is_loaded(id)) {
    eval_js(clean_js(grab_js(get_elm(id))));
    mark_as_loaded(id);
   }
  }
 };
};
window['xvn'] = xvn;

module-compiled.js
window.xvn=function(d){var f=d.createElement("script").tagName,e={};return{load_module:function(c){if(!(c&&e[c])){var a;var b;if(c)b=d.getElementById(c);if((b=b)&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}a=(a=a)?a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"):void 0;if(a){a=["{",a,"}"].join("");eval(a)}e[c]=true}}}};

index.html
<!DOCTYPE html>
<html>
<head>
 <title>Module Loading Tests</title>
 <script type="text/javascript" src="./module-compiled.js"></script>
</head>
<body>
 <div id="click-me-man" onclick="my_xvn.load_module('mod1');">Load Mod1</div>

 <script type="text/javascript" id="mod1">
 /*
  alert("Hey!");
 */
 </script>

 <script type="text/javascript">
  var my_xvn = new xvn(document);
 </script>
</body>
</html>

2009-12-03

Making X binary files of Y bytes

I had to make some dummy files for testing uncompressed zip archives.

Here's what I did:
$ ruby -e 'File::open("hex.bin", "wb") {|f| 1.upto(5*1024) {|b| f.write("\xff")}}'
$ hexdump -Cv hex.bin | tail
00001370  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00001380  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00001390  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013a0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013b0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013c0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013d0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013e0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000013f0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00001400
$ ruby -e '1.upto(10) {|i| s = sprintf("%04d",i); `ln -s ./hex.bin ./bin#{s}.bin`}'
$ find . -name "bin*.bin" -print | zip -r -0 -X source -@
$ du -abL
5120    ./bin0007.bin
5120    ./bin0004.bin
52202   ./source.zip
5120    ./bin0006.bin
5120    ./bin0002.bin
5120    ./bin0008.bin
5120    ./bin0005.bin
5120    ./bin0009.bin
5120    ./bin0001.bin
5120    ./hex.bin
5120    ./bin0003.bin
5120    ./bin0010.bin
112618  .
$

Awesome.

2009-11-19

VirtualBox-3.0.12-54655-Win

I'm running 32-bit Windows XP with CentOS x86_64 in a VirtualBox-3.0.10-54097, with an intel Core 2 Duo (VT-x support enabled via BIOS).

I just tried to upgrade to VirtualBox-3.0.12-54655; but, the CentOS box wouldn't even start up! I even tried live CDs!

Here's the evil error:
NMI Watchdog detected LOCKUP on CPU0

Maybe there's a compatibility issue with the kernel?
$ echo "`cat /etc/*-release` `uname -m` : `uname -sr`"
CentOS release 5.4 (Final) x86_64 : Linux 2.6.18-164.6.1.el5

Everything went great with my i386 version, though...
$ echo "`cat /etc/*-release` `uname -m` : `uname -sr`"
CentOS release 5.4 (Final) i686 : Linux 2.6.18-164.el5

I don't know...

2009-05-12

IE8 Crippled My Computer

I have no idea what the root cause was.

Since Microsoft has put IE8 in the "critical" updates section, I decided to give it a try.

Why the hell not? They had the chutzpah to label it as "critical"; so, it must be good enough, right?

Well, it didn't sit well with my computer. After installing, just starting up IE8 made the computer unable to recognize the local area network connection!

And, on top of that, trying to shut-down the computer in any respectable manner (erm, I mean via Windows shutdown) was impossible! Windows would go into "shut-down limbo", which is basically a doldrums where no application can be run. Windows kindly reminded me over-and-over that it is currently trying to shut-down (via a nice pop-up, of course)...

Yeah, so, uh... Nature called. Time to remove this "critical" update.

After a bit of searching I came across this excellent knowledge base article. This goes into various ways of how to revert back to the previous version of IE. I went with the "Add/Remove Programs" approach and had no problems what so ever.

I found it very pleasently suprising that Microsoft apparently has put a lot of thought into removal of IE8!

Oh, but, for anyone who installed XP SP3 after IE8, it seems that you're in for a ride. Apparently you've got to remove SP3 before it becomes even possible to remove IE8...

wtf?

2009-04-24

limiting grep searches

this post is awesome.

find . -name "*.extension" | xargs -L10 grep -i "search"

runs very quickly on my system.

2009-01-14

Google Desktop Crippled My Computer!

Google Desktop really is quite a handy-dandy little tool for Microsoft Windows. It basically provides a search engine for one's own computer. It goes through and indexes files, folders, mail, Web history, etc. The user can then search for file names and/or content.

Well, that's pretty sweet, right?
Not for me, at least...

For some reason, after installing, all control panels (.cpl files) stopped working. Rundll32.exe shows up in the processes for a split-second, then disappears.

In addition, cmd.exe is also mysteriously killed... Starting a command prompt from the WINDOWSKEY-R combo doesn't even work!!

Immediately after uninstallation, everything goes back to normal.

What the hell!?

2008-12-05

String.Format Trap - Incorrect Handling of Input

Developers sometimes fall into this trap:
string input = "First Last";
string fmt = "Your {0} is: " + input;
string out = String.Format(fmt, "name");
There's nothing wrong with this code, right?

WRONG.

What happens if input looks like this?
string input = "First Last {1}";
Well, String.Format will look for another parameter... BANG!
An exception is thrown.

The correct way to use input parameters for String.Format (and all printf-like functions, for that mater) is as follows:
string input = "First Last";
string fmt = "Your {0} is: {1}";
string out = String.Format(fmt, "name", input);
Put the input as a parameter to the format and at least this type of attack can be avoided!

2008-11-24

Click Log - Part 2

QUEST

To gather unobtrusively click-through data.


STRATEGY: Client-Side

Use javascript to attach handlers for onclick and onsubmit events to all links and forms on a page.

When a link is clicked or a form submitted, an HTTP request of some kind will be initiated. The request just needs to be sent; we won't care about the response, too much...

The HTTP request destination could be on a different domain; so, it is not practical to use "AJAX". There following are possible ways of sending extra HTTP requests from within a page:
- <style> tags
- <script> tags
- <img> tags
- <iframe> tags
- <object> tags
- <embed> tags
- change a CSS background-image

Maybe there are some other ways...


STRATEGY: Server-Side

Create a "page" (probaly a script in perl or PHP) that will return appropriate content based on the way in which the request was created--e.g., a <style> would return CSS, an <img> a 1x1 transparent GIF, etc.

2008-11-19

Click Log - Part 1

Background

My Web site uses urchin.js to track PVs, UUs, etc.
Google provides an account code in the form of "UA-xxxxxx-x".
Presumably, all data should be collected and processed based on this code.

I want to find out which links/forms users are actually clicking.

Problem

I created a new account code and started using _trackPageview provided by ga.js. But, this caused major problems! My site's UUs more than doubled--yet, PVs stayed consistent--for the urchin account! We can't have that, now, can we? There was obviously something wrong...

Upon talking with Google, it seems that the system is not meant to have more than one account on one page at the same time.

So, I decided to get my hands dirty and find out how ga.js works. Basically, it looks like both urchin.js and ga.js use the same cookie values, and that is probably what was messing things up...

Conclusion

According to the documentation, ga.js has some ways of setting cookie names and the URI of a local GIF file. I'll have to look into that to see if I can't get two accounts working on the same page.

At any rate, I think that I'm going to have to start writing my own click log. It might prove to be rather educational!