My question is about hosting multiple ZnServers in Pharo:
I put together an app that is mapped to default delegate. I also want to be able to generate and serve files for download so I thought I would also host a file serve with ZnStaticFileServerDelegate class. I tried as so:
| a b |
a := ZnServer new.
a := ZnServer startOn: 80.
a delegate: (
ZnStaticFileServerDelegate new
directory: '/var/www' asFileReference;
prefixFromString: 'files';
yourself).
along with:
b := ZnServer new.
b := ZnServer startOn: 80.
b default delegate map: #display to: WebRedirect new.
But when I try to run both, I get the error mess that ZnStaticFileServerDelegate #doesNotUnderstand default delegate map.
Is there any way to host both on the same image?
Related
Here's a fresh version of my code. It is now even closer, if I look at the updated version in Resource Hacker it tells me that the group has nine icons, which is true, that they're 16.8 million color, which is true, that they're all 16 x 16, which is not true, and that it can't actually show me what they look like, which is annoying. Also that they all have an ordinal name of 150 if that means anything to anyone.
procedure TForm1.Button1Click(Sender: TObject);
var vResHandle: THandle;
MyIcon: TMemoryStream;
begin
// Get the icon.
MyIcon := TMemoryStream.Create;
MyIcon.LoadFromFile('icon.ico');
// Set the position in the memory stream to the start.
MyIcon.Seek(0, soFromBeginning);
// Get the handle.
vResHandle := BeginUpdateResource('exec.exe', False);
if vResHandle=0 then
raise Exception.Create('System giving error message: '
+ SysErrorMessage(GetLastError));
try
// Change the icon.
if not UpdateResource(vResHandle
, RT_GROUP_ICON
, PChar('MAINICON')
, LANG_NEUTRAL
, MyIcon.Memory
, MyIcon.Size)
then
raise Exception.Create('System giving error message: '
+ SysErrorMessage(GetLastError));
finally
EndUpdateResource(vResHandle, False);
end;
MyIcon.Free;
end;
Short version of how it works: So. Before you try to put any bit of data into an .exe file using a resource update you must be sure it will fit. Icon files are difficult. In this particular case I needed to modify the structure of the .ico file and split it into different pieces and do a resource update separately on each. I didn't do that. I was like someone trying to fit a seven-fingered hand into one finger of a five-fingered glove.
How the thing works is explained in the code but what exact effect it has on Windows must be explained up here.
(1) Although the application icon (in the top left corner of your main form) can be set to be completely different from the main icon for the program, it seems like it's overwritten to be in line with the main icon once you do the update. 99% of the time this would be exactly what you want. If it isn't what you want you'll have to take it from here.
(2) File Explorer caches this stuff so hard that you won't see any change in how the icon's displayed there unless you restart Explorer. This is fine for my purposes, if it's a problem for you then again you'll have to solve it yourself, sorry.
(3) This is NOT an answer to that frequently-asked question, "How do I change the toolbar icon of my Pascal-based application while it's running?" Because you can't do a resource update on an executable that's being executed, whether your own or another.
(4) If you're going to use this in Pascal, then you're going to need to add Windows to your uses statement. If you're going to use this in any language in other than Pascal but you're still using Windows, then it will translate kind of easily because it's basically telling the Windows OS to do stuff, but you'll have to find out which library or whatever lets you do that and what syntax it wants you to use.
(5) If you're wondering about how to do the thing the other way round and extract an .ico file from an executable file, then this is of course theoretically possible and has been done by cleverer people then me and indeed done in Pascal. (Download Resource Hacker for an example.) But you can't just do this by reversing my code as it were, there are obstacles in your way. Doing it this way Windows has built in facilities for me to do this. Doing it the other way it seems like it doesn't.
procedure TForm1.Button1Click(Sender: TObject);
var vResHandle: THandle;
MyIcon: TMemoryStream;
i,j: integer;
s: string;
ImageCount: Word;
ImageSize: DWord;
ab, m: TMemoryStream;
const HeaderSize = 6;
IcoEntrySize = 16;
ResEntrySize = 14;
begin
// Short explanation. An icon file consists of (a) a six-byte header which
// includes among other things information about how many icons are in
// the file; (b) sixteen bytes of metadata for each icon; (c) the icons.
// But that's how icons are stored as files. As executable resources,
// Windows considers that (a) and (b) are one resource but (c) is a different
// resource, indeed one resource for each image, so we have to split the icon
// file up and do several resource updates.
// It also requires only fourteen bytes of metadata per entry: instead of the
// last parameter being a double word referring to the position of the image
// in memory, it's a single word conferring an ID.
// Initialize stuff
MyIcon := TMemoryStream.Create;
ab := TMemoryStream.Create;
m := TMemoryStream.Create;
// Get the icon
MyIcon.LoadFromFile('icon.ico');
// Get the handle for the resource update..
vResHandle := BeginUpdateResource('test.exe', False);
// We skip forward in the memory stream to where Windows keeps the image count and read it.
MyIcon.Seek(4,soFromBeginning);
ImageCount:=MyIcon.ReadWord;
// Go back to the beginning ...
MyIcon.Seek(0,soFromBeginning);
// We read the directory information into ab, modifying its format as we do so.
for j:=1 to HeaderSize do ab.WriteByte(MyIcon.ReadByte);
for i:=1 to ImageCount do
begin
for j:=1 to IcoEntrySize - 4 do ab.WriteByte(MyIcon.ReadByte);
MyIcon.ReadDWord; // To skip over it.
ab.WriteWord(i);
end;
// Update the icon directory with ab, which is now in the correct format.
UpdateResource(vResHandle
, RT_GROUP_ICON
, PChar('MAINICON')
, LANG_NEUTRAL
, ab.Memory
, ab.Size);
// Now the size of each icon is contained as a double word in the directory
// entries for each item, so we use that to cut the remainder of the memory
// stream into chunks and update them one at a time.
for i := 1 to ImageCount do
begin
m := TMemoryStream.Create;
ab.Seek(HeaderSize+(i-1)*ResEntrySize+8,soFromBeginning);
ImageSize:=ab.ReadDWord;
for j:=1 to ImageSize do m.WriteByte(MyIcon.ReadByte);
UpdateResource(vResHandle
, RT_ICON
, MAKEINTRESOURCE(i)
, LANG_NEUTRAL
, m.Memory
, m.Size);
m.Free;
end;
EndUpDateResource(vResHandle,False);
MyIcon.Free;
ab.Free;
end;
Suppose I'm using a third party network library that I don't want to modify. It uses the standard http.Request interface to make some HTTPS requests.
I'm running it on an embedded Linux instance that doesn't have a certificate roots installed, and the only directory I can access is /data. This means you get the error:
Get https://example.com/: x509: failed to load system roots and no roots provided
Is there any way to actually provide roots? As far as I can tell Go looks in these directories for X509 certificate roots (see also):
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/pki/tls/cacert.pem", // OpenELEC
}
var certDirectories = []string{
"/system/etc/security/cacerts", // Android
}
As I said, I don't have access to those, and the root pool seems to be private so you can't append to it:
var (
once sync.Once
systemRoots *CertPool
)
func systemRootsPool() *CertPool {
once.Do(initSystemRoots)
return systemRoots
}
Is this just impossible with Go?
Something like this seems to work (I actually only need one certificate chain, which you can get from Firefox easily by clicking on the SSL lock icon, More Information, View Certificate, Details, Export, Change type to "X509 certificate with chain (PEM)".
func initCerts() error {
certs := x509.NewCertPool()
pemData, err := ioutil.ReadFile("api.example.crt")
if err != nil {
return err
}
certs.AppendCertsFromPEM(pemData)
newTlsConfig := &tls.Config{}
newTlsConfig.RootCAs = certs
defaultTransport := http.DefaultTransport.(*http.Transport)
defaultTransport.TLSClientConfig = newTlsConfig
return nil
}
I'm not certain, but the docs suggest that you must do that before using any TLS functions, so I put it at the start of my main().
Is there a way to make a script where I can do stuff like $this->EE->db (i.e. using Expression Engine's classes, for example to access the database), but that can be run in the command line?
I tried searching for it, but the docs don't seem to contain this information (please correct me if I'm wrong). I'm using EE 2.4 (the link above should point to 2.4 docs).
The following article seems to have a possible approach: Bootstrapping EE for CLI Access
Duplicate your index.php file and name it cli.php.
Move the index.php file outside your DOCUMENT_ROOT. Now, technically, this isn’t required, but there’s no reason for prying
eyes to see your hard work so why not protect it.
Inside cli.php update the $system_path on line 26 to point to your system folder.
Inside cli.php update the $routing['controller'] on line 96 to be cli.
Inside cli.php update the APPPATH on line 96 to be $system_path.'cli/'.
Duplicate the system/expressionengine directory and name it system/cli.
Duplicate the cli/controllers/ee.php file and name it cli/controllers/cli.php.
Finally, update the class name in cli/controllers/cli.php to be Cli and remove the methods.
By default EE calls the index method, so add in an index method to do what you need.
#Zenbuman This was useful as a starting point although I would add I had issues with all of my requests going to cli -> index, whereas I wanted some that went to cli->task1, cli->task2 etc
I had to update *system\codeigniter\system\core\URI.php*so that it knew how to extract the parameters I was passing via the command line, I got the code below from a more recent version of Codeigniter which supports the CLI
// Is the request coming from the command line?
if (php_sapi_name() == 'cli' or defined('STDIN'))
{
$this->_set_uri_string($this->_parse_cli_args());
return;
}
// Let's try the REQUEST_URI first, this will work in most situations
and also created the function in the same file
private function _parse_cli_args()
{
$args = array_slice($_SERVER['argv'], 1);
return $args ? '/' . implode('/', $args) : '';
}
Also had to comment out the following in my cli.php file as all routing was going to the index method in my cli controller and ignoring my parameters
/*
* ~ line 109 - 111 /cli.php
* ---------------------------------------------------------------
* Disable all routing, send everything to the frontend
* ---------------------------------------------------------------
*/
$routing['directory'] = '';
$routing['controller'] = 'cli';
//$routing['function'] = '';
Even leaving
$routing['function'] = '';
Will force requests to go to index controller
In the end I felt this was a bit hacky but I really need to use the EE API library in my case. Otherwise I would have just created a separate application with Codeigniter to handle my CLI needs, hope the above helps others.
I found #Zenbuman's answer after solving my own variation of this problem. My example allows you to keep the cron script inside a module, so if you need your module to have a cron feature it all stays neatly packaged together. Here's a detailed guide on my blog.
I am running play on multiple machines in our datacenter. We loadbalance the hell out of everything. On each play node/VM I'm using Apache and an init.d/play script to start and stop the play service.
The problem is that our play websites are hosted on shared network storage. This makes deployment really nice, you deploy to one place and the website is updated on all 100 machines. Each machine has a mapped folder "/z/www/PlayApp1" where the play app lives.
The issue is that when the service starts or stops the server.pid file is being written to that network location where the apps files live.
The problem is that as I bring up 100 nodes, the 100th node will override the PID file with it's pid and now that pid file only represents the correct process ID for 1 out of 100 nodes.
So how do I get play to store the pid file locally and not with the app files on the network share? I'll need each server's PID file to reflect that machines actual process.
We are using CentOS (Linux)
Thanks in advance
Josh
According to https://github.com/playframework/play/pull/43 it looks like there is a --pid_file command line option; it might only work with paths under the application root so you might have to make directories for each distinct host (which could possibly be symlinks)
I have 0 experience with Play so hopefully this is helpful information.
I don't even think it should run a second copy, based on the current source code. The main function is:
public static void main(String[] args) throws Exception {
File root = new File(System.getProperty("application.path"));
if (System.getProperty("precompiled", "false").equals("true")) {
Play.usePrecompiled = true;
}
if (System.getProperty("writepid", "false").equals("true")) {
writePID(root);
}
:
blah blah blah
}
and writePID is:
private static void writePID(File root) {
String pid = ManagementFactory.getRuntimeMXBean().getName().split("#")[0];
File pidfile = new File(root, PID_FILE);
if (pidfile.exists()) {
throw new RuntimeException("The " + PID_FILE + " already exists. Is the server already running?");
}
IO.write(pid.getBytes(), pidfile);
}
meaning it should throw an exception when you try to run multiple copies using the same application.path.
So either you're not using the version I'm looking at or you're discussing something else.
It seems to me it would be a simple matter to change that one line above:
File root = new File(System.getProperty("application.path"));
to use a different property for the PID file storage, one that's not on the shared drive.
Although you'd need to be careful, root is also passed to Play.int so you should investigate the impact of changing it.
This is, after all, one of the great advantages of open source software, inasmuch as you can fix the "bugs" yourself.
For what it's worth, I'm not a big fan of the method you've chosen for deployment. Yes, it simplifies deployment but upgrading your servers is an all-or-nothing thing which will cause you grief if you accidentally install some dodgy software.
I much prefer staged deployments so I can shut down non-performing nodes as needed.
Change your init script to write the pid to /tmp or somewhere else machine-local.
If that is hard, a symlink might work.
my names's Carlos Im from Brazil.
Im trying to open a file like this:
image1.picture.loadfromfile('\\ntmemo01\c$\ozzy2.bmp');
but it doesnt work. Im receving the exception
class EFOpenError with message "Cannot open file '\ntmemo01\c$\ozzy2.bmp' Access denied."
Thanks,
Carlos
You have to use double backslashes.
image1.picture.loadfromfile('\\ntmemo01\c$\ozzy2.bmp');
If you still get the exception, then the file is inaccessible from your application.
The first thing you should do is making sure, that you can access the file using the Windows Explorer.
Just type it into the Run dialog of the start menu (WinKey+R) and see what happens. If it doesn't work, make it work there first and then go back to your program.
Is the C: drive on ntmemo01 shared? If it's not shared, you can't access it. If it's shared but requires a user name and password to access, you'll have to access it differently. You can map a drive letter to it, providing a user name and password in the process:
const
RemoteName = '\\ntmemo01\C$';
UserName = 'yourusername';
Password = 'yourpassword';
function MapNetworkDrive: Boolean;
var
NetRes: TNetResource;
Res: DWord;
begin
Result := True;
FillChar(NetRes, SizeOf(TNetResource), 0);
NetRes.dwType := RESOURCETYPE_DISK;
NetRes.lpRemoteName := PChar(RemoteName);
NetRes.lpLocalName := 'H:'; // Whatever drive letter you want
Res := WNetAddConnection2(NetRes, PChar(Password), PChar(UserName), 0);
Result := (Res = NO_ERROR);
end;
To unmap afterwards:
function UnMapNetworkDrive: Boolean;
var
Res: DWord;
begin
Res := WNetCancelConnection2(PChar('H:'), 0, True); // same drive letter as above
Result := (Res + NO_ERROR);
end;
As mentioned by DR, the filename requires double backslashes for a UNC path
The access denied message suggests the you do not have permission to access the C$ share on the ntmemo01 computer.
C$ a is hidden administrative share are you sure the current user account has the correct permissions? You make have to the map a drive first, as suggested by Ken White
Administrive shares are disabled by default in Windows Vista and Windows 7, unless you join a domain. You can be enable them manually as follows.
Click on the start button and in the search box type ‘regedit’ and hit enter.
Browse to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System.
Add a new DWORD called LocalAccountTokenFilterPolicy and give it a value of 1.
Reboot and yer done!
Source: http://www.paulspoerry.com/2007/05/09/how-to-access-administrative-shares-on-vista-c/
when you copy this exact same string in windows explorer, is the file opened?
Otherwise it might be a rights problem, as suggested by the error.